超市付款扫一扫,免费wifi扫一扫,添加好友扫一扫。 二维码就像是神一般的存在!! 可是到底二维码是个啥呢?
QRCode.jpg
用某种特定的几何图形按照一定规律在平面分布的黑白相间的图形记录数据符号信息的。所谓生成二维码就是根据给定的信息,将其按照二维码的编码方式来生成一张图片,而读取二维码就是识别二维码图形里面存储的数据。
信息获取:比如说获取个人资料、wifi密码 手机电商:用户扫码 加好友:QQ微信扫一扫
从iOS7开始集成了二维码的生成和读取功能。此前被广泛使用的zbarsdk目前不支持64位处理器,而在15年的2月起,苹果是不允许不支持64位处理器的APP上架的。
常用两种方式:一种是从图片中识别,最低支持iOS8.0,另一种是利用摄像头扫描识别,需要真机设备。
1、导入CoreImage框架
import CoreImage
该框架专用于做一些图片处理操作,如滤镜效果,毛玻璃,美颜相机等效果
2、通过滤镜CIFilter生成二维码 其实主要步骤就是创建滤镜将数据输入到滤镜中,再由滤镜输出二维码图片。可细分为以下步骤。
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字样
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
工欲善其事必先利其器,先把需要的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)
}
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创建会话,连接输入和输出
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)
2.4启动会话,让输入开始采集数据,输出对象开始处理数据
session!.startRunning()
2.5调用扫描方法 为了测试,设定在touchesBegan方法中调用扫描方法
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
startScan()
}
最后再友情提示,若是升级到iOS10.0以上,需要在plist文件中设置启动相机权限,否则会导致crash
3、处理二维码扫描结果
若觉得书读百遍不如实地演练,可以戳下面github地址吖: 二维码Demo传送门 若觉得文字读来太过枯燥无味,可以戳下面小姐姐视频讲解传送门吖: 视频讲解传送门