显示出来扫描二维码的界面,很简单:
AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:nil];
self.captureOutput = [[AVCaptureMetadataOutput alloc] init];
[_captureOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
self.captureSession = [[AVCaptureSession alloc] init];
[_captureSession setSessionPreset:AVCaptureSessionPresetHigh];
if ([_captureSession canAddInput:captureInput]) {
[_captureSession addInput:captureInput];
}
if ([_captureSession canAddOutput:_captureOutput]) {
[_captureSession addOutput:_captureOutput];
}
_captureOutput.metadataObjectTypes = @[AVMetadataObjectTypeQRCode];
CGRect rect = CM(30, 100, Screen_Width - 60, 300);
AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:_captureSession];
previewLayer.frame = self.view.bounds;
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.view.layer addSublayer:previewLayer];
[_captureSession startRunning];
按上述代码设置之后,就可以扫描二维码啦,但是这个是全屏扫描的,貌似无论你的previewLayer的frame设置的多大都是全屏扫描的。
而且一般设计时,有一个扫描区域之类的,怎么设置呢?
AVCaptureMetadataOutput 有一个属性 rectOfInterest
就是设置元数据识别搜索的区域。
这个属性有点问题,不是普通的CGRect,四个值都需要在0~1之间。
搜了好久看大家都是试的。最后看到rectOfInterest设置问题这个问题的回答后,才知道的。
AVCaptureVideoPreviewLayer 有个方法
- (CGRect)metadataOutputRectOfInterestForRect:(CGRect)rectInLayerCoordinates
可以看看这个方法的说明:
描述就是:把一个在previewlayer坐标系中的rect 转换成 一个在metadataoutputs坐标系中的rect。
这个方法需要的rect参数是我们系统坐标系中的rect.
SO 。这就得到了我们需要的rectOfInterest。
验证:
我们在 startRuning 之后(这里之前写的是'之前',但是下面的代码是在startRuning之后的)添加如下代码:
[self.view.layer addSublayer:previewLayer];
[_captureSession startRunning];
CGRect intertRect = [previewLayer metadataOutputRectOfInterestForRect:rect];
CGRect layerRect = [previewLayer rectForMetadataOutputRectOfInterest:intertRect];
NSLog(@"%@, %@",NSStringFromCGRect(intertRect),NSStringFromCGRect(layerRect));
_captureOutput.rectOfInterest = intertRect;
运行设备是:iPhone6,(375, 667) 设置的rect (想要的扫描区域) (30, 100, 315, 300)
打印结果:
intertRect {{0.14992503748125927, 0.080629685157421305}, {0.4497751124437781, 0.8387406296851575}},
layerRect {{29.999999999999943, 99.999999999999972}, {315.00000000000006, 300}}
- (CGRect)rectForMetadataOutputRectOfInterest:(CGRect)rectInMetadataOutputCoordinates
这个方法是把_captureOutput.rectOfInterest坐标系转成preview layer 的坐标系的rect。
通过打印数据可以验证成功,真机实验也是正确的。
补充:设置中间透空区域
UIView *maskView = [[UIView alloc] initWithFrame:CM(0, 64, Screen_Width, Screen_Height - 64)];
maskView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
[self.view addSubview:maskView];
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRect:CM(0, 0, Screen_Width, Screen_Height)];
[maskPath appendPath:[[UIBezierPath bezierPathWithRoundedRect:CM(30, 100 - 64, Screen_Width - 60, 300) cornerRadius:1] bezierPathByReversingPath]];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.path = maskPath.CGPath;
maskView.layer.mask = maskLayer;
demo地址:github