专栏首页谦谦君子修罗刀swift手撕二维码一、简介二、二维码综合案例

swift手撕二维码一、简介二、二维码综合案例

超市付款扫一扫,免费wifi扫一扫,添加好友扫一扫。 二维码就像是神一般的存在!! 可是到底二维码是个啥呢?

QRCode.jpg

一、简介

1、概念

用某种特定的几何图形按照一定规律在平面分布的黑白相间的图形记录数据符号信息的。所谓生成二维码就是根据给定的信息,将其按照二维码的编码方式来生成一张图片,而读取二维码就是识别二维码图形里面存储的数据。

2、场景

信息获取:比如说获取个人资料、wifi密码 手机电商:用户扫码 加好友:QQ微信扫一扫

3、生成方式

从iOS7开始集成了二维码的生成和读取功能。此前被广泛使用的zbarsdk目前不支持64位处理器,而在15年的2月起,苹果是不允许不支持64位处理器的APP上架的。

4、二维码读取

常用两种方式:一种是从图片中识别,最低支持iOS8.0,另一种是利用摄像头扫描识别,需要真机设备。

二、二维码综合案例

案例1、生成二维码

1、导入CoreImage框架 import CoreImage 该框架专用于做一些图片处理操作,如滤镜效果,毛玻璃,美颜相机等效果

2、通过滤镜CIFilter生成二维码 其实主要步骤就是创建滤镜将数据输入到滤镜中,再由滤镜输出二维码图片。可细分为以下步骤。

  • 实例化二维码滤镜
  • 恢复滤镜的默认属性
  • 将字符串转换成NSData数据
  • 通过KVC设置滤镜inputMessage数据
  • 获得滤镜输出的图像
  • 将CIImage转换成UIImage,并放大显示
  • 通过位图创建高清图片

2.1 创建滤镜 在创建滤镜的时候使用带name的函数,后面跟的值一定要写成“CIQRCodeGenerator”

let filter = CIFilter(name:"CIQRCodeGenerator")

2.2 设置滤镜的输入数据

滤镜的输入数据必须要转换成NSData数据,然后通过KVC方式设置滤镜的inputMessage数据

let data = "456".data(using:String.Encoding.utf8)
filter?.setValue(data, forKey: "inputMessage")

2.3 从二维码中获取结果

为了代码的健壮性,在操作之前先判断从滤镜中输出的图片是否为nil。若有值,将CIImage图片转换成UIImage类型的图片。

if let image = filter?.outputImage {
            let resultImage = UIImage(ciImage: image)
            print(resultImage.size)
            //显示结果
            qrCoderImageView.image = resultImage
        }

生成二维码的基本步骤就到此为止,但是如果此时运行代码,会发现生成的二维码是非常模糊 的。

依上图所示,计算机获取到的二维码图片大小为(23,23),而我们给要显示它的ImageView设定的范围必定远远超过该大小,所以就会造成图片拉伸而导致的显示不清晰的效果。 解决方法就是将滤镜输出的图片按照比例放大20倍。

if var  image = filter?.outputImage {
            let transform = CGAffineTransform(scaleX: 20, y: 20)
            image = image.transformed(by: transform)
            let resultImage = UIImage(ciImage: image)
            print(resultImage.size)
            //显示结果
            qrCoderImageView.image = resultImage
        }

此时可以看到输出size的值为(460,460),二维码瞬间清晰了不少呢。 用手机软件扫描该二维码会显示456字样

案例2、自定义二维码

1、简述

啥叫自定义二维码呢,其实就是指给二维码做添加图片或改变颜色的操作。 改变二维码的颜色或者添加背景图片不会对二维码扫描造成影响,可是若在二维码上添加了前景则必定会遮挡住二维码的某些部分,那么我们又怎么确保能正确的扫描到二维码指定的地址去呢? 那么这里就不得不提到“纠错率”的概念了。二维码中有三个角用来做扫描定位使用,只要保证这三个角不被遮挡,就算其他部分有被遮挡的地方,也能根据其他部分计算出被遮挡的数据。

2、设置纠错率

如上所示,也是通过KVC的形式来设置滤镜的inputCorrectionLevel。 value有如下几个:L水平表示7%的字码可以修正,M水平表示15%的字码可以修正,Q水平代表25%的字码可以修正,H水平代码30%的字码可以纠正。 水平越高,二维码的信息的细节分散得越开。但是,也并不是说水平越高就越好,因为随着level的升高,扫描的时间也会越长。

        filter?.setValue("M", forKey: "inputCorrectionLevel")

下面来做图片处理的操作,给二维码加上前景图片。 创建一个方法,传入二维码图片与要加入的前景图片作为参数,返回值为一张加了前景图的二维码图片。

 func getNewImage(sourceImage:UIImage,center:UIImage) -> UIImage {
 }

在方法中首先要通过传入的二维码图片开启图像的上下文

let size = sourceImage.size
  //开启图形上下文
UIGraphicsBeginImageContext(size)

之后绘制大小图片,大图片即为二维码,设置大小边框为0,0,宽度,高度

  sourceImage.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))

小图片要放在大图片中心,可以给出固定的宽高,而它的位置则放在(二维码宽度或高度-小图片的宽度或高度)* 0.5

  //绘制小图片
        let width:CGFloat = 80
        let height:CGFloat = 80
        let x :CGFloat = (size.width-width)*0.5
        let y:CGFloat = (size.height-height)*0.5
        
        center.draw(in: CGRect(x: x, y: y, width: width, height: height))

接下来取出结果图片,关闭上下文,返回结果就能够完成这个方法了

 //取出结果图片
   let resultImage = UIGraphicsGetImageFromCurrentImageContext()
   //关闭上下文
   UIGraphicsEndImageContext()
   //返回图片
   return resultImage!

回到touchsBegin方法中,创建需要嵌入到二维码中的小图片,并用二维码图片调用封装好的方法得到返回的图片

 let center = UIImage(named: "img_1.jpg")
  resultImage = getNewImage(sourceImage: resultImage, center: center!)

当然,为了在未编辑时回收键盘,可以加上一句

        view.endEditing(true)

image.png

案例3、识别二维码

工欲善其事必先利其器,先把需要的imageView组件和UIButton方法及需要识别的图片统统加入到Xcode。 1、在按钮的实现方法中,首先要获取需要识别的图片

 @IBOutlet weak var souceImageView: UIImageView!
    
    @IBAction func detectorQRCode(_ sender: Any) {
        // 1、获取需要识别的图片
        let image = souceImageView.image
        }

2、第二步进行识别。 2.1先创建一个二维码的探测器

探测器的类型为二维码类型,场景可以由自己选择,在这里我们选择用识别度较高的CIDetectorAccuracyHigh

 //2.1创建一个二维码探测器
        let dector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: [CIDetectorAccuracy:CIDetectorAccuracyHigh])

2.2探测二维码特征 获取到二维码的所有特征并遍历,在这里获取特征的方法传入的参数是一个CIImage,所以要先将二维码图片转换CIImage类型。遍历特征,将取出的特征转换为CIQRcodeFeature即二维码特征。

 let ciImage = CIImage(image: image!)
let features = dector?.features(in: ciImage!)
        for feature in features! {
            let qrFeature = feature as!CIQRCodeFeature
            print(qrFeature.messageString as Any)
        }

案例4、扫描二维码

1、二维码的扫描动画 底部添加一个View,约束:w:200,h:200,水平和垂直都居中 在View上面添加一个imageView,存放扫描框的图片。约束:上下左右为0 在View上面添加一个imageView,存放线的图片。在现实中,扫描线是会随时间而发生变化的。最好的方法就是改变图片底部的约束。为它做出动画的效果。约束条件为:左:0,下:0,与View等宽等高。

将底部的约束拖入到代码中,命名为toButtom

 @IBOutlet weak var toButtom: NSLayoutConstraint!

接下来要为扫描线设置动画,创建一个类扩展自ScanQRCode,添加一个扫描方法。 在扫描的时候,线是从最上方往最下方开始扫描,因此底部的约束最开始的时候是停留在最上方。可以将背景View拖入代码中给底部约束做参考。进行重新约束之后添加动画。而且要求动画一直循环滚动。

extension ScanQRCode {
    func startScanAnimation() -> () {
        toButtom.constant = scanBgView.frame.size.height
        //重新布局
        view.layoutIfNeeded()
        //修改
        toButtom.constant = -scanBgView.frame.size.height
        //添加动画
        UIView.animate(withDuration: 1) {
            UIView.setAnimationRepeatCount(MAXFLOAT)
            self.view.layoutIfNeeded()
        } 
    }

接下来在viewDidAppear方法中调用

override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        startScanAnimation()
    }

至此已经有了扫描的功能,但是因为约束的关系,扫描线会超出页面,因此,我们要将背景View绘制成Clip to Bounds,切除背景图以外的图片。

2、二维码的扫描功能实现 输入仪器有很多种,比如说摄像仪器,话筒仪器,因此在扫描之前要先设置输入仪器为摄像仪器,将摄像仪器作为输入设备再识别图片,识别出来之后通过会话将源数据处理对象连接起来,接着启动会话,让输入仪器开始采集数据,输出对象开始处理数据。这就是二维码扫描功能的实现。

创建一个方法,用来做扫描操作 2.1设置输入

  • 导入设备所需要的框架
import AVFoundation
  • 创建扫描的方法
 func startScan() -> () {}
  • 获取摄像头
 let device = AVCaptureDevice.default(for: AVMediaType.video)
  • 把摄像头设备当做输入设备
var input:AVCaptureDeviceInput?
        do {
            input = try AVCaptureDeviceInput(device: device!)
        } catch {
            print(error)
            return
        }

2.2设置输出

   let output = AVCaptureMetadataOutput()
  • 设置结果处理的代理
        output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
  • 实现代理方法
 //扫描到结果之后调用
extension ScanQRCode:AVCaptureMetadataOutputObjectsDelegate{
    func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
        print("test")
    }
}

2.3创建会话,连接输入和输出

  • 因为输入和输出到最后都放到了session中处理,所以可以在方法之外定义一个全局的session变量
var session:AVCaptureSession?
session = AVCaptureSession()
        //并非所有的设备都能被添加进来,所以要做判断
        if session!.canAddInput(input!)&&session!.canAddOutput(output){
            session!.addInput(input!)
            session!.addOutput(output)
        }else{
            return
        }
  • 设置二维码可以识别的编码制度
output.metadataObjectTypes = [AVMetadataObject.ObjectType.qr]
  • 添加视频预览图层 (让用户可以看到界面)
        let layer = AVCaptureVideoPreviewLayer(session:session!)
        layer.frame = view.layer.bounds
        view.layer.addSublayer(layer)
  • 插入边框
 // 这样是没有二维码的边框的,所以插入边框
  view.layer.insertSublayer(layer, at: 0)
  • 此时还是会有一个背景色,所以到storybord中将scanBgView的背景颜色去除

2.4启动会话,让输入开始采集数据,输出对象开始处理数据

   session!.startRunning()

2.5调用扫描方法 为了测试,设定在touchesBegan方法中调用扫描方法

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        startScan() 
    }

最后再友情提示,若是升级到iOS10.0以上,需要在plist文件中设置启动相机权限,否则会导致crash

3、处理二维码扫描结果

若觉得书读百遍不如实地演练,可以戳下面github地址吖: 二维码Demo传送门 若觉得文字读来太过枯燥无味,可以戳下面小姐姐视频讲解传送门吖: 视频讲解传送门

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 静态内存区域解析

    通常在代码中产生的bug,往往是源于概念不清晰。知己知彼百战不殆,对内存这块了如指掌,能极大优化代码的性能。 一、内存四区建立流程讲解 ? 如上图所示,首先操作...

    谦谦君子修罗刀
  • storyboard中改变TabBar的字体颜色

    一、用storyboard建立的Tab bar,不能改变它的字体颜色。 如果用storyboard建立TabBarController,那么想要选择Bar T...

    谦谦君子修罗刀
  • 程序员面试闪充 -- 性能优化

    CPU 和GPU 关于绘图和动画有两种处理方式CPU(中央处理器)和GPU(图形处理器),CPU的工作都在软件层面,而GPU的在硬件层面。 总的来说,可以使用...

    谦谦君子修罗刀
  • 二维码会使用完么?

    现在生活中总是会使用到二维码,在支付中、博客的推广图片上、各种各样的商品推广,都有着二维码的身影,二维码已经是我们的日常生活中有着不可替代的便捷信息载体,近几日...

    Debug客栈
  • 还可以这样玩!一键生成「专属二维码」,就靠这 4 款小程序

    今天,知晓程序(微信号 zxcx0101)给你推荐 4 款二维码生成器小程序,让你轻松玩转二维码。

    知晓君
  • 一行python代码生成酷炫/恶搞/表白二维码(内附源码)

    很多人都扫过二维码,大部分人的印象都是黑白相间的小方块,实际上二维码是运用了计算机图像处理技术,组合编码原理的一种编码,其用黑白像素点的图像来代表二进制中的“1...

    行哥玩Python
  • 走近科学:二维码真的安全吗

    二维码自普及以来,无论是从它的便捷性,还是其安全问题,一直都是公众关注的焦点问题。“码”时代来势迅猛,不可阻挡,似乎一夜之间,二维码即遍布各电商平台、商场、网站...

    FB客服
  • 标签打印软件如何生成DataMatrix码

    随着二维码应用的普及,二维码类型也随着时代的发展越来越多了起来,其中DataMatrix码也逐渐被大家接收并投入使用,下面我们来详细了解一下其制...

    用户5759861
  • 5 行 Python 代码生成自定义二维码

    随着互联网和智能移动设备不断普及,二维码(Quick Response code)已经成为世界上应用最为广泛的信息载体之一。生成二维码的工具也层出不穷,但多数需...

    用户2769421
  • 如何扫描二维码显示表格内容

    二维码可以用网址、数字、字母、汉字等表示, 通过扫描二维码,来表示一些特定的信息。最近有朋友咨询,扫描二维码,内容是用表格呈现出来的,该如何制作?如下图:

    用户5746110

扫码关注云+社区

领取腾讯云代金券