二维码在App中的使用越来越广泛,如支付软件用二维码确定支付信息,社交软件用二维码添加联系人,下载页面用二维码链接到App Store下载链接等。二维码能存储汉字、数字、字母和图片等信息,因此二维码的应用领域很广泛。
二维码(2-dimensional bar code)是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的技术。在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”“1”比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图像输入设备或光电扫描设备自动识读以实现信息自动处理。
常用的二维码是QR码(QR Code),是一种矩阵二维码符号码。它的示例和结构如图4-13所示。
图4-13 QR码示例和结构
扫描二维码已成为实际开发中的常用功能,接下来就围绕如何扫描二维码进行详细的分步骤介绍,具体如下。
1. 导入头文件
由于扫描二维码的相关类都包含在AVFoundation框架中,所以要导入头文件,具体代码如下:
#import <AVFoundation/AVFoundation.h>
2.创建会话
由于iOS系统用一个会话对象管理扫描过程,所以在扫描时第1步是创建会话对象。会话使用AVCaptureSession类。创建会话之前首先要定义一个属性保存会话对象,代码如下:
@property (nonatomic, strong) AVCaptureSession *session;
然后创建会话对象,并赋值给这个属性,代码如下:
self.session = [[AVCaptureSession alloc]init];
3. 创建输入设备
要扫描二维码,必须用到摄像头,也就是输入设备。输入设备要关联到会话,指定扫描会话所使用的输入设备。输入设备用AVCaptureDeviceInput类表示,具体代码如例4-11所示。
【例4-11】ViewController.m文件中创建输入对象
//创建一个普通设备 AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; //根据普通设备创建一个输入设备 AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil]; //将输入设备关联到会话 if ([self.session canAddInput:input]) { [self.session addInput:input]; }
在例4-11中,首先创建了一个普通设备,并指定普通设备的类型为视频输入,创建出的普通设备一般是后置摄像头。然后根据这个普通设备创建输入设备。最后判断该输入设备是否能成功关联到会话,如果可以,则执行关联。
4. 创建输出对象
在输入设备扫描到二维码以后,输出对象负责识别二维码包含的信息,并将识别到的信息传递出来。输出对象使用AVCaptureMetadataOutput类表示。示例代码如例4-12所示。
【例4-12】ViewController.m文件中创建输出对象
//创建一个输出对象 AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc]init]; //输出对象关联到会话 if ([self.session canAddOutput:output]) { [self.session addOutput:output]; } //设置元数据类型,是QR二维码 output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode]; //设置代理,得到解析结果 [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
例4-12的代码中,第2行代码创建了输出对象,第4~6行代码判断该输出对象是否可以关联到会话,如何可以,就执行关联。第8行代码设置输出对象的元数据类型为AVMetadataObjectTypeQRCode,也就是二维码类型。第10行代码设置输出对象的代理,以后由代理来处理输出对象识别到的信息。
5. 创建显示扫描的层
在摄像头扫描时,需要创建一个特殊的CALayer来显示摄像头拍摄到的视频内容,这个类是CALayer的子类,叫做AVCaptureVideoPreviewLayer。要创建这个AVCaptureVideoPreviewLayer对象,首先要定义一个属性保存它,示例代码如下。
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *preLayer;
然后创建这个层对象,并赋值给该属性,示例代码如例4-13所示。
【例4-13】ViewController.m文件中创建显示扫描的层
//创建一个特殊的层 self.preLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:self.session]; //设置尺寸并添加到视图树 self.preLayer.frame = self.view.bounds; [self.view.layer addSublayer:self.preLayer];
在例4-13的代码中,首先创建了一个层对象,然后设置层的尺寸与控制器视图的尺寸相同,最后将扫描层添加到控制器的视图树中。
6. 开启会话
到这一步,会话已经准备好了,使用会话对象的startRunning方法开启会话,则摄像头被打开,扫描开始。具体代码如下:
[self.session startRunning];
- 获取二维码信息
识别到的二维码信息由输出对象交给代理对象处理。AVCaptureMetadataOutput类规定一个协议,叫做AVCaptureMetadataOutputObjectsDelegate,该协议只有一个方法,该方法的参数metadataObjects里就包含了识别到的二维码信息。该方法定义如下:
-(void)captureOutput:(AVCaptureOutput *)output didOutputMetadataObjects:(NSArray<__kindof AVMetadataObject *> *)metadataObjects fromConnection:(AVCaptureConnection *)connection
在创建输出对象时已将控制器设置为输出对象的代理。控制器要成为它的代理,首先必须遵守AVCaptureMetadataOutputObjectsDelegate协议,示例代码如下:
@interface ViewController ()<AVCaptureMetadataOutputObjectsDelegate>
然后要实现代理方法,示例代码如例4-14所示。
【例4-14】ViewController.m文件中实现代理方法
-(void)captureOutput:(AVCaptureOutput *)output didOutputMetadataObjects:(NSArray<__kindof AVMetadataObject *> *)metadataObjects fromConnection:(AVCaptureConnection *)connection{ //停止会话 [self.session stopRunning]; //移除显示扫描层 [self.preLayer removeFromSuperlayer]; if (metadataObjects.count > 0) { AVMetadataMachineReadableCodeObject *object = [metadataObjects firstObject]; NSLog(@"%@",object.stringValue); } }
在例4-14所示的代理方法中,停止会话,即停止扫描。然后将创建的显示扫描的层从控制器的视图树上移除。首先判断metadataObjects中是否有对象,如果有则取出该对象,并将对象的stringValue字符串值打印到控制器。
编写完代码,执行程序,就可以在控制台打印出识别到的二维码信息了。
摘自《iOS开发项目化经典教程》