今天有时间整理下原生的二维码的生成与读取。
准备工作
- 导入
<CoreImage/CoreImage.h>
,生成二维码用 - 导入
<AVFoundation/AVFoundation.h>
,读取二维码用 - 协议
AVCaptureMetadataOutputObjectsDelegate
,这是有关摄像设备输出的相关代理 -
注意:扫描二维码的时候,要在info.plist文件中添加字段,否则会崩溃(iOS10之后的隐私权限问题)
<key>NSPhotoLibraryUsageDescription</key>
<string>App需要您的同意,才能访问相册</string>
<key>NSCameraUsageDescription</key>
<string>App需要您的同意,才能访问相机</string>
二维码的生成
1.生成二维码的步骤:
1)导入CoreImage框架
2)通过滤镜CIFilter生成二维码
代码如下
调用下方根据字符串生成二维码的方法即可获得二维码
// 生成二维码
- (UIImage *)createImageWithString:(NSString *)string{
// 1.实例化二维码滤镜
CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
// 2.恢复滤镜的默认属性(因为滤镜可能保存上一次的属性)
[filter setDefaults];
// 3.讲字符串转换为NSData
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];*
// 4.通过KVO设置滤镜inputMessage数据
[filter setValue:data forKey:@"inputMessage"];
// 5.通过了滤镜输出的图像
CIImage *outputImage = [filter outputImage];
// 6.因为生成的二维码模糊,所以通过createNonInterpolatedUIImageFormCIImage:outputImage来获得高清的二维码图片
UIImage *image = [self getErWeiMaImageFormCIImage:outputImage withSize:200];
return image;
}
// 获取高清二维码图片
- (UIImage *)getErWeiMaImageFormCIImage:(CIImage *)image withSize:(CGFloat) size {
CGRect extent = CGRectIntegral(image.extent);
CGFloat scale = MIN(size/CGRectGetWidth(extent), size/CGRectGetHeight(extent));
// 1.创建bitmap;
size_t width = CGRectGetWidth(extent) * scale;
size_t height = CGRectGetHeight(extent) * scale;
CGColorSpaceRef cs = CGColorSpaceCreateDeviceGray();
CGContextRef bitmapRef = CGBitmapContextCreate(nil, width, height, 8, 0, cs, (CGBitmapInfo)kCGImageAlphaNone);
CIContext *context = [CIContext contextWithOptions:nil];
CGImageRef bitmapImage = [context createCGImage:image fromRect:extent];
CGContextSetInterpolationQuality(bitmapRef, kCGInterpolationNone);
CGContextScaleCTM(bitmapRef, scale, scale);
CGContextDrawImage(bitmapRef, extent, bitmapImage);
// 2.保存bitmap到图片
CGImageRef scaledImage = CGBitmapContextCreateImage(bitmapRef);
CGContextRelease(bitmapRef);
CGImageRelease(bitmapImage);
return [UIImage imageWithCGImage:scaledImage];
}
运行结果
[图片上传失败...(image-bbce1-1542166399362)]
😊😊😊😊😊😊我是可爱的分割线😊😊😊😊😊😊
二维码的生成还是比较简单的,下面让我们来看下二维码的扫描
😊😊😊😊😊😊我是可爱的分割线😊😊😊😊😊😊
二维码的读取
1.读取二维码的步骤:
1)读取二维码需要导入AVFoundation框架(上方准备工作的时候已经说了)
2)利用相机识别二维码中的内容(只能是真机)
3)会话将相机采集到的二维码图像转换成字符串数据
2.原生扫描中用到的几个类
AVCaptureDevice // 拍摄设备
AVCaptureDeviceInput // 输入设备
AVCaptureMetadataOutput // 元数据输出
AVCaptureSession // 拍摄会话
AVCaptureVideoPreviewLayer // 视频预览图层
代码如下
- (void)readQRCode{
// 1.实例化拍摄装备
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// 2.设置输入设备
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
// 3.设置元数据输出
AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; // 设置代理
// 4.添加拍摄会话
self.session = [[AVCaptureSession alloc] init];
[self.session addInput:input]; // 添加会话输入
[self.session addOutput:output]; // 添加会话输出
[output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]]; // 设置输出数据类型(需要将元数据输出添加到会话后才能制定元数据类型,否则会报错)
// 5.视频预览图层
self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:_session]; // 传递session是为了告诉图层将来显示什么内容
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; // 显示方式
// 设置videoGravity,顾名思义就是视频播放时的拉伸方式,默认是AVLayerVideoGravityResizeAspect
// AVLayerVideoGravityResizeAspect 保持视频的宽高比并使播放内容自动适应播放窗口的大小。
// AVLayerVideoGravityResizeAspectFill 和前者类似,但它是以播放内容填充而不是适应播放窗口的大小。最后一个值会拉伸播放内容以适应播放窗口.
// 因为考虑到全屏显示以及设备自适应,这里我们采用fill填充
self.previewLayer.frame = self.view.bounds;
[self.view.layer insertSublayer:self.previewLayer atIndex:0]; // 将图层插入当前图层
// 6.启动会话
[self.session startRunning];
}
AVCaptureMetadataOutputObjectsDelegate 的代理方法
/**
扫描结果处理
@param captureOutput 输出数据源
@param metadataObjects 扫描结果数组
@param connection 用于协调输入与输出之间的数据流
*/
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
// 1.判断扫描结果的数据是否存在
if ([metadataObjects count] > 0) {
// 2.如果存在数据,则停止会话
[self.session stopRunning];
// 3.删除预览图层
[self.previewLayer removeFromSuperlayer];
AVMetadataMachineReadableCodeObject *metadataObject = metadataObjects[0];
// AVMetadataMachineReadableCodeObject 是AVMetadataObject的具体子类定义的特性检测一维或二维条形码。
// AVMetadataMachineReadableCodeObject代表一个单一的照片中发现机器可读的代码。这是一个不可变对象描述条码的特性和载荷。
// 在支持的平台上,AVCaptureMetadataOutput输出检测机器可读的代码对象的数组
NSString *stringValue = metadataObject.stringValue;
if ([stringValue containsString:@"http"]) {
// 如果是字符串,则打开连接
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:stringValue] options:[NSDictionary dictionary] completionHandler:^(BOOL success) {
if (success) {
NSLog(@"成功");
}
}];
}else{
NSLog(@"普通字符串:%@",stringValue); // 可以将字符串放到需要用到的地方(比如label)
}
}
}