二维码知识点总结
01-二维码简介
1.概念
- 二维码:用某种特定的集合图形按照一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号的信息
- 生成二维码:根据给定的信息,将其按照二维码的编码方式生成一张图片
- 读取二维码:识别二维码图像里面存储的数据
2.使用场景
- 信息获取(名片/WiFi密码/资料)
- 手机电商(用户扫码/手机直接购物下单)
- 加好友(QQ/微信/扫一扫加好友)
- 手机支付(扫描商品二维码,通过银行或第三方支付提供的手机端通道完成支付)
3.生成方式
- 从iOS7开始集成了二维码的生成和读取功能
- 此前被广泛使用的zbarsdk目前不支持64位处理器
- 2015年2月1日起,不允许不支持64位处理器的APP上架
4.二维码读取
- 直接从图片中识别,最低支持iOS8.0
- 利用摄像头扫描识别,需要真机设备
02-生成/识别/读取二维码
1.生成二维码
- 导入CoreImage框架
- 一些图片处理操作的功能,都是用这个框架实现,比如:滤镜效果/毛玻璃/美颜相机等
- #import <CoreImage/CoreImage.h>
- 通过滤镜CIFilter生成二维码
- 实例化二维码滤镜 CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
- 恢复滤镜的默认属性 [filter setDefault];
- 将字符串转换成NSData NSData *data = [@"木喳喳的夏天" dataUsingEncoding:NSUTF8StringEncoding];
- 通过KVC设置滤镜inputMessage数据 [filter setValue:data forKey:@"inputMessage"];
- 获得滤镜输出的图像,大小默认是23*23 CIImage *outputImage = [filter outputImage];
- 将CIImage转换成UIImage,并放大显示 return [UIImage imageWithCIImage:outputImage scale:20.0 orientation:UIImageOrientationUp];
- 通过位图创建高清图片
/**
根据CIImage生成指定大小的高清UIImage
:param: image 指定CIImage
:param: size 指定大小
:returns: 生成好的图片
*/
private func createBigImage(image: CIImage, size: CGFloat) -> UIImage {
let extent: CGRect = CGRectIntegral(image.extent)
let scale: CGFloat = min(size/CGRectGetWidth(extent), size/CGRectGetHeight(extent))
// 1.创建bitmap;
let width = CGRectGetWidth(extent) * scale
let height = CGRectGetHeight(extent) * scale
let cs: CGColorSpaceRef = CGColorSpaceCreateDeviceGray()!
let bitmapRef = CGBitmapContextCreate(nil, Int(width), Int(height), 8, 0, cs, 0)!
let context = CIContext(options: nil)
let bitmapImage: CGImageRef = context.createCGImage(image, fromRect: extent)
CGContextSetInterpolationQuality(bitmapRef, CGInterpolationQuality.None)
CGContextScaleCTM(bitmapRef, scale, scale);
CGContextDrawImage(bitmapRef, extent, bitmapImage);
// 2.保存bitmap到图片
let scaledImage: CGImageRef = CGBitmapContextCreateImage(bitmapRef)!
return UIImage(CGImage: scaledImage)
}
2.自定义二维码
- 所谓自定义二维码,就是指给二维码添加一些图片(前景或背景图片),或者改变下颜色
- 可以添加前景图片的前提是因为二维码具备一定的"容错率"
- 如果二维码被部分遮挡,可以根据其他部分,计算出遮挡部分的内容;
- 但是要保证三个角不能被遮挡;
- 三个角用作扫描定位使用(可能用户倒着拍/斜着拍等)
- 通过KVC设置滤镜的inputCorrectionLevel(容错率)
- @"L",@"M",@"Q",@"H"中的一个
- L水平:7%的字码可被修正
- M水平:15%的字码可被修正
- Q水平:25%的字码可被修正
- H水平:30%的字码可被修正
func createImage(bgImage: UIImage?, iconImage: UIImage?) -> UIImage?
{
if bgImage == nil || iconImage == nil
{
return nil
}
// 1.开启图片上下文
UIGraphicsBeginImageContext(bgImage!.size)
// 2.绘制背景
bgImage!.drawInRect(CGRect(origin: CGPointZero, size: bgImage!.size))
// 3.绘制图标
let w:CGFloat = 50
let h = w
let x = (bgImage!.size.width - w) * 0.5
let y = (bgImage!.size.height - h) * 0.5
iconImage!.drawInRect(CGRect(x: x, y: y, width: w, height: h))
// 4.取出图片
let newImage = UIGraphicsGetImageFromCurrentImageContext()
// 5.关闭上下文
UIGraphicsEndImageContext()
return newImage
}
3.识别二维码
- 识别图片二维码
- 创建一个上下文 let context = CIContext()
- 创建一个探测器 let detector = CIDetector(ofType:CIDetectorTypeQRCode,context:context,option:[CIDetectorAccuracy:CIDetectorAccuracyHigh])
- 转换图片为CIImage let image = CIImage(CGImage:sourceImage.CGImage)
- 获取探测器识别的图像特征 let features = detector.featuresInImage(image)
- 遍历图片特征,获取数据
- var tempArray = [String]()
- for feature in features {}
- let resultFeature: CIQRCodeFeature = feature as! CIQRCodeFeature
- tempArray.append(resultFeature.messageString)
- 绘制识别到的二维码边框
private class func drawRectInImage(qrFeature: CIQRCodeFeature, image: UIImage) -> UIImage
{
let size = image.size
UIGraphicsBeginImageContext(size)
image.drawInRect(CGRectMake(0, 0, size.width, size.height))
// 反转坐标系(因为是别的二维码坐标是相对于图片的坐标, 坐标系是以, 左下角为0, 0, 所以需要上下翻转坐标系)
let context = UIGraphicsGetCurrentContext()
CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0, -image.size.height);
let path: UIBezierPath = UIBezierPath(rect: qrFeature.bounds)
UIColor.redColor().set()
path.lineWidth = 6
path.stroke()
let resultImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resultImage
}
3.2 扫描二维码
- 二维码扫描动画
- 二维码扫描功能
- 实例化拍摄设备
- AVCaptureDevice *device = [AVCaputureDevice defalutDeviceWithMediaType:AVMediaTypeVideo];
- 设置输入设备
- AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
- 设置元数据输出处理对象
- 实例化拍摄元数据输出
- AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
- 设置输出数据代理
- [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
- 添加拍摄会话
- 实例化拍摄会话
- AVCaptureSession *session = [[AVCaptureSession alloc] init];
- 添加会话输入
- [session addInput:input];
- 添加会话输出
- [session addOutput:output];
- 设置输出数据类型,需要将元数据输出添加到会话后,才能指定元数据类型,否则报错
- [output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];
- 视频预览图层(不是必须)
- 实例化预览图层
- AVCaptureVideoPreviewLayer *preview = [AVCaptureVideoPreviewLayer layerWithSession:_session];
- preview.frame = self.view.bounds;
- 将图层插入当前视图
- [self.view.layer addSublayer:preview];
- self.previewLayer = preview;
- 启动会话
- 监听元数据处理后的结果
- 当扫描到数据就会执行captureOutput:didOutputMetadataObjects:fromConnection
- 二维码边框描绘
- 提示:获取到二维码后,可以获得二维码的四个角,但是需要使用预览图层进行坐标转换
- previewLayer.transformedMetadataObjectForMetadataObject(object as! AVMetadataObject)
- 创建CAShapLayer,并设置path
- 二维码扫描区域限定
- 设置兴趣点
- 注意:每个参数的取值都是对应的比例
- 注意:坐标系是横屏状态下的坐标系
- 而且值域范围是0-1
- output.rectOfInterest = rect
3.3 使用注意
- 读取二维码需要导入AVFoundation框架
- 利用摄像头识别二维码中的内容(模拟器不行)