参考文章:
http://stackoverflow.com/questions/32401364/how-do-i-use-the-metadataoutputrectofinterestforrect-method-and-rectofinterest-p
http://www.jianshu.com/p/3fb24fc7b415
项目地址:https://github.com/tiancanfei/ADQRScanView.git
iOS原生扫码使用还是很方便的,只不过是iOS7以后的技术,最近用到把一些注意事项和踩过的坑记录下
iOS原生扫码的实现就是构建一个扫描回话(session)
,完成一次扫码过程就是完成一次会话(从会话的开启到会话结束
)。
一、扫描会话构建
1、获取扫描设备
扫描必然用到摄像头,我们需要获取摄像头
self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
2、创建设备输入和设备输出
//将设备设置为输入设备
self.input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:nil];
//创建设备输出
self.output = [[AVCaptureMetadataOutput alloc]init];`
3、设置输出代理(AVCaptureMetadataOutputObjectsDelegate)
代理只有一个方法需要实现其作用就是扫码成功的回调(- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection)
//设置设备代理和执行线程
[self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
4、创建扫描会话,并且将设备的输入和输出添加到会话
会话是输入和输出的桥梁,只有将设备输出和输入加入到会话设备才能正常工作
//创建会话
self.session = [[AVCaptureSession alloc]init];
//设置视频输入每一帧图像质量
[self.session setSessionPreset:AVCaptureSessionPreset1920x1080];
//添加设备输入到会话
if ([self.session canAddInput:self.input])
{
[self.session addInput:self.input];
}
//添加设备输出到会话
if ([self.session canAddOutput:self.output])
{
[self.session addOutput:self.output];
}
5、设置扫码类型
//设置识别二维码或者条码,这里可以识别二维码和条形码
(只有AVMetadataObjectTypeQRCode是二维码,其他常量都是条码)
self.output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode,AVMetadataObjectTypeCode39Code,AVMetadataObjectTypeCode128Code,AVMetadataObjectTypeCode39Mod43Code,AVMetadataObjectTypeEAN13Code,AVMetadataObjectTypeEAN8Code,AVMetadataObjectTypeCode93Code];
6、设置预览框(就是那个半透明的背景框)
//给会话创建设置预览框(就是可视范围)
self.preview =[AVCaptureVideoPreviewLayer layerWithSession:self.session];
self.preview.videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.layer insertSublayer:self.preview atIndex:0];
7、开启扫描会话
//开始扫描
[self.session startRunning];
到此,其实已经可以扫码了。
二、扫描范围限制
以上虽然实现了扫码功能,但是到底是比较粗糙,下面做一些改善。
通常我们看到诸如微信等的扫码都会有一个扫码框,只有在扫描范围内才能扫描成功,这就是扫描范围的限制,可以让扫描更加方便。当然,限制扫描范围绝对不仅仅是为了扫描方便,更重要一点是优化性能,设置扫描范围,设备在捕获信息的时候需要处理的图像就会小一点,处理起来自然速度回比较快。但是如何限制扫描范围确实让我头疼了一段时间,几经波折呀,最后找到了上面的标注的两篇文章才正真解决了。
扫描范围的限制其实只需要设置输出设备的rectOfInterest
属性,而这个属性的值是一个CGRect
而且x,y,w,h的取值都在0~1之间是一个比例
,查阅了很多博客,最后发现只有下面的方法最靠谱,感觉也最正规,就是调用系统官方提供的metadataOutputRectOfInterestForRect
方法,那种使用手动计算比例的方法,一旦改变预览框(preview)
的frame(默认的预览框都是全屏幕的),就不怎么好使了,也许是我没有理解吧。
//计算有效的扫描范围
CGRect rectOfInterest = [self.preview metadataOutputRectOfInterestForRect:visibleRect];
//限制有效的扫描范围
[self.output setRectOfInterest:rectOfInterest];
三、预览框设置(半透明背景框)
通常我们看到的扫描框都是周边是半透明的,只有有效扫描范围内才是可以去透明的。其实办法简单就是给预览框(preview)
加一个遮盖并且把有效扫描范围的rect
挖空即可。
办法直接上代码
#pragma mark 设置背景
- (void)setPreviewBackground{
if (self.previewBackgroundSetted) {
return;
}
self.previewBackgroundSetted = YES;
CALayer *coverLayer = [[CALayer alloc] init];
coverLayer.frame = self.bounds;
coverLayer.backgroundColor = [kScanViewBackgroundColor colorWithAlphaComponent:kBackgroundOpacity].CGColor;
[self.preview addSublayer:coverLayer];
UIBezierPath *outerBorderPath = [UIBezierPath bezierPathWithRect:self.bounds];
UIBezierPath *innderBorderPath = [UIBezierPath bezierPathWithRect:self.visibleRect];
[outerBorderPath appendPath:innderBorderPath];
CAShapeLayer *visibleRectLayer = [CAShapeLayer layer];
visibleRectLayer.fillRule = kCAFillRuleEvenOdd;
visibleRectLayer.path = outerBorderPath.CGPath;
coverLayer.mask = visibleRectLayer;
}
注意:
1、在构建扫描会还的时候,只有将输出设备添加到会话以后才可以设置输出扫码类型,否则会因为奔溃。
下面是完整代码地址有兴趣的可以看一下,或者直接使用。
2、不要把scanview的创建放在viewdidload方法中,容易出现卡顿,将其创建放在viewdidapear中,在start之前添加一个菊花等待,在start后关闭结束。
https://github.com/tiancanfei/ADQRScanView.git