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 条评论
登录 后参与评论

相关文章

来自专栏小轻论坛

Photoshop最新版上线!这些功能亮了!

Adobe 爸爸发布最新版本Photoshop CC 2019了!那它都有哪些新变动呢?接下来让我们一探究竟吧!

2542
来自专栏程序员的知识天地

web前端学习:HTML5十个新特性

                   :刻度尺/度量衡,描述数据所处的阶段,红色(危险)=>黄色(警告)=>绿色(优秀)

1881
来自专栏我的博客

Eclipse设置背景色和字体大小

一、设置Eclipse代码编辑背景色(保护自己从这里开始) 1、打开window / Preference,弹出Preference面板 2、展开Genera...

4068
来自专栏前端小叙

一个非常好用的文字滚动的案例,鼠标悬浮可暂停

网上找了很多,万变不离其宗,写法核心都是一样的,在这里我给大家总结一下,可收藏备用。 html: <div class="scroll"> <ul cl...

3658
来自专栏CDA数据分析师

一看就会又超级实用的Excel10大技巧

本期责编:Sophie 普通人需要用到的Excel的功能不到其全部功能的10%。也就是说,对于绝大部分的用户来说,只要掌握了几个必须懂的Excel表格的基本操作...

2028
来自专栏知晓程序

如何在小程序中绘制图表?

1362
来自专栏AndroidTv

一起撸个简单粗暴的Tv应用主界面的网格布局控件(上)

这一篇是真的隔了好久了~~,也终于可以喘口气来好好写博客了,这段时间实在是忙不过来了,迭代太紧。好,废话不多说,进入今天的主题。

3256
来自专栏Android群英传

记一次代码中毒急救

1212
来自专栏练小习的专栏

伪类以及伪元素的一些使用小技巧

在浏览器版本越来越高的情况下,很多以前顾及到兼容问题不敢使用的html以及css属性现在已经很普遍的在使用了。比如一些伪类和伪元素。这里稍微提一下在实际工作中用...

2209
来自专栏用户2442861的专栏

Chrome开发者工具不完全指南:(三、性能篇)

 卤煮在前面已经向大家介绍了Chrome开发者工具的一些功能面板,其中包括Elements、Network、Resources基础功能部分和Sources进阶功...

1452

扫码关注云+社区

领取腾讯云代金券