1、QRCode
在 iOS7 以前,在 iOS 中实现二维码和条形码扫描,我们所知的有,两大开源组件 ZBar 与 ZXing。iOS7 之后可以利用系统原生 API 生成二维码, iOS8 之后可以生成条形码, 系统默认生成的颜色是黑色。
- 1、ZBar 在扫描的灵敏度上,和内存的使用上相对于 ZXing 上都是较优的,但是对于 “圆角二维码” 的扫描确很困难。
- 2、ZXing 是 Google Code 上的一个开源的条形码扫描库,是用 java 设计的,连 Google Glass 都在使用的。但有人为了追求更高效率以及可移植性,出现了 c++ port。Github 上的 Objectivc-C port,其实就是用 OC 代码封装了一下而已,而且已经停止维护。这样效率非常低,在 instrument 下面可以看到 CPU 和内存疯涨,在内存小的机器上很容易崩溃。
- 3、AVFoundation 无论在扫描灵敏度和性能上来说都是最优的,所以毫无疑问我们应该切换到 AVFoundation,需要兼容 iOS 6 或之前的版本可以用 ZBar 或 ZXing 代替。
在 iOS8 + 系统中使用相机需要在 Info.plist 中添加 Privacy - Camera Usage Description,并设置其值。使用相册需要在 Info.plist 中添加 Privacy - Photo Library Usage Description,并设置其值。
按照下图在 Info.plist 文件中将 Localization native development region 的值改为 China。如果不设置此项弹出的相册页面中显示的按钮等为英文菜单。
2、系统原生二维码
2.1 扫描二维码
官方提供的接口非常简单,直接看代码,主要使用的是 AVFoundation。
// 包含头文件 #import <AVFoundation/AVFoundation.h> // 遵守协议 <AVCaptureMetadataOutputObjectsDelegate> // 输入输出的中间桥梁 @property (nonatomic, strong) AVCaptureSession *session; // 扫描窗口 @property (nonatomic, strong) UIImageView *scanView; // 创建扫描视图窗口,自定义方法 - (void)createdScanView { CGFloat margin = 50; CGRect scanFrame = CGRectMake(margin, margin + 20, self.view.bounds.size.width - margin * 2, self.view.bounds.size.width - margin * 2); self.scanView = [[UIImageView alloc] initWithFrame:scanFrame]; self.scanView.image = [UIImage imageNamed:@"scan_bg2"]; [self.view addSubview:self.scanView]; // 创建扫描 [self startScan]; } // 创建扫描,自定义方法 - (void)startScan { // 获取摄像设备 AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; // 创建输入流 AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil]; // 创建输出流 AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init]; // 设置代理,在主线程里刷新 [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; // 设置有效扫描区域 output.rectOfInterest = [self getScanCropWithScanViewFrame:self.scanView.frame readerViewBounds:self.view.bounds]; // 初始化链接对象 self.session = [[AVCaptureSession alloc] init]; // 设置采集率,高质量 [self.session setSessionPreset:AVCaptureSessionPresetHigh]; [self.session addInput:input]; [self.session addOutput:output]; // 设置扫码支持的编码格式(如下设置条形码和二维码兼容) output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code]; AVCaptureVideoPreviewLayer *layer = [AVCaptureVideoPreviewLayer layerWithSession:self.session]; layer.videoGravity = AVLayerVideoGravityResizeAspectFill; layer.frame = self.view.layer.bounds; [self.view.layer insertSublayer:layer atIndex:0]; // 开始捕获 [self.session startRunning]; } // 设置扫描区域的比例关系,自定义方法 - (CGRect)getScanCropWithScanViewFrame:(CGRect)scanViewFrame readerViewBounds:(CGRect)readerViewBounds { CGFloat x, y, width, height; x = scanViewFrame.origin.y / readerViewBounds.size.height; y = scanViewFrame.origin.x / readerViewBounds.size.width; width = scanViewFrame.size.height / readerViewBounds.size.height; height = scanViewFrame.size.width / readerViewBounds.size.width; return CGRectMake(x, y, width, height); } // 获取扫描结果,AVCaptureMetadataOutputObjectsDelegate 协议方法 - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection { if (metadataObjects.count > 0) { // 停止扫描 [self.session stopRunning]; AVMetadataMachineReadableCodeObject *metadataObject = [metadataObjects objectAtIndex:0]; // 获取扫描结果 NSString *resultString = metadataObject.stringValue; // 输出扫描字符串 [[[UIAlertView alloc] initWithTitle:@"扫描成功" message:resultString delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil] show]; } }
一些初始化的代码加上实现代理方法便完成了二维码扫描的工作,这里我们需要注意的是,在二维码扫描的时候,我们一般都会在屏幕中间放一个方框,用来显示二维码扫描的大小区间,这里我们在AVCaptureMetadataOutput 类中有一个 rectOfInterest 属性,它的作用就是设置扫描范围。这个 CGRect 参数和普通的 Rect