做项目的过程中,或许都遇到过扫码的需求,从最早通过zxin和zbar的解码库,自定义扫码界面完成基本的扫码需求。到近期因为公司项目对引用的三方库有严格的过审要求,从而想到基于AVFoundation框架的自定义扫码解析。这里做一个简单的小计。充当个备胎,以备不时之需。
step 1:导入框架
AVFoundation/AVFoundation.h/AVFoundation/AVCaptureDevice.h/AVFoundation/AVMediaFormat.h
step2:加入代理AVCaptureMetadataOutputObjectsDelegate/AVCaptureVideoDataOutputSampleBufferDelegate
step3:实例对象
/** 设备 */
@property (nonatomic, strong) AVCaptureDevice * device;
/** 输入输出的中间桥梁 */
@property (nonatomic, strong) AVCaptureSession * session;
/** 相机图层 */
@property (nonatomic, strong) AVCaptureVideoPreviewLayer * previewLayer;
/** 扫描支持的编码格式的数组 */
@property (nonatomic, strong) NSMutableArray * metadataObjectTypes;
这里的metadataObjectTypes数组是为了区分二维码和条形码的不同解析
/**
* MARK:根据条码状态返回不同的解码数组
*/
- (NSMutableArray * )setMetaDataOutPutWithBarcode :(BOOL)isBarcode{
NSMutableArray *metadataOutput_Arr = [[NSMutableArray alloc]init];
if (!isBarcode) {
metadataOutput_Arr = [NSMutableArray arrayWithObjects:AVMetadataObjectTypeAztecCode, AVMetadataObjectTypePDF417Code, AVMetadataObjectTypeQRCode, nil];
// >= iOS 8
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_7_1) {
[metadataOutput_Arr addObjectsFromArray:@[ AVMetadataObjectTypeDataMatrixCode]];
}
}else{
metadataOutput_Arr = [NSMutableArray arrayWithObjects:AVMetadataObjectTypeCode128Code, AVMetadataObjectTypeCode39Code, AVMetadataObjectTypeCode39Mod43Code, AVMetadataObjectTypeCode93Code, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeUPCECode, nil];
// >= iOS 8
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_7_1) {
[metadataOutput_Arr addObjectsFromArray:@[AVMetadataObjectTypeInterleaved2of5Code, AVMetadataObjectTypeITF14Code]];
}
}
return metadataOutput_Arr;
}
下面看看初始化
/**
* 扫描初始化
*/
- (void)capture{
//获取摄像设备
self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
//创建输入流
AVCaptureDeviceInput * input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:nil];
//创建输出流
self.metadataOutput = [[AVCaptureMetadataOutput alloc] init];
{//规范化扫描有效范围
CGFloat scanView_w = 0;
CGFloat scanView_H = 0;
if (self.isBarcode) {
scanView_w = FTNScreenW-20*2;
scanView_H = 156.0;
}else{
scanView_w = scanView_Width;
scanView_H = scanView_Height;
}
//设置你需要解析的范围(确保范围在你自定义框的内部,避免未进扫描框就可以解析条码了)
[self.metadataOutput setRectOfInterest:CGRectMake(((FTNScreenH-scanView_H)/2)/FTNScreenH, ((FTNScreenW-scanView_w)/2)/FTNScreenW, scanView_H/FTNScreenH, scanView_w/FTNScreenW)];
}
//设置代理 在主线程里刷新
[self.metadataOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
//创建视频输出流
AVCaptureVideoDataOutput * videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
//设置代理在子线程里刷新
dispatch_queue_t queue = dispatch_queue_create("myQueue", NULL);
[videoDataOutput setSampleBufferDelegate:self queue:queue];
//设置像素格式
videoDataOutput.videoSettings = [NSDictionary dictionaryWithObject:
[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey];
//高质量采集率
self.session.sessionPreset = AVCaptureSessionPresetHigh;
[self.session addInput:input];
[self.session addOutput:self.metadataOutput];
[self.session addOutput:videoDataOutput];
self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
self.previewLayer.frame = CGRectMake(0, 0, FTNScreenW, FTNScreenH);
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
self.previewLayer.backgroundColor = [UIColor yellowColor].CGColor;
[self.view.layer addSublayer:self.previewLayer];
//设置扫描支持的编码格式(如下设置条形码和二维码兼容)
// metadataOutput.metadataObjectTypes = self.metadataObjectTypes;
self.metadataOutput.metadataObjectTypes = [self setMetaDataOutPutWithBarcode:NO];
//开始捕获
[self.session startRunning];
//自动调焦 (解决条形码扫描难)
//先进行判断是否支持控制对焦
if (self.device.isFocusPointOfInterestSupported) {
NSError *error = nil;
//对cameraDevice进行操作前,需要先锁定,防止其他线程访问
[self.device lockForConfiguration:&error];
[self.device setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
//操作完成后,记得进行unlock。
[self.device unlockForConfiguration];
}
if (self.device.isExposurePointOfInterestSupported) {
NSError *error = nil;
//对cameraDevice进行操作前,需要先锁定,防止其他线程访问
[self.device lockForConfiguration:&error];
[self.device setExposureMode:AVCaptureExposureModeAutoExpose];
//操作完成后,记得进行unlock。
[self.device unlockForConfiguration];
}
if (self.device.isAutoFocusRangeRestrictionSupported) {
NSError *error = nil;
//对cameraDevice进行操作前,需要先锁定,防止其他线程访问
[self.device lockForConfiguration:&error];
[self.device setAutoFocusRangeRestriction:AVCaptureAutoFocusRangeRestrictionNear];
//操作完成后,记得进行unlock。
[self.device unlockForConfiguration];
}
}
最后,扫描结果的返回方法
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
if (metadataObjects.count > 0) {
[self.session stopRunning];
AVMetadataMachineReadableCodeObject * metadataObject = metadataObjects.firstObject;
for (AVMetadataMachineReadableCodeObject * metadataObject in metadataObjects) {
NSLog(@"-----jie guo ---- %@",metadataObject.stringValue);
}
self.returnScanBarCodeValue(metadataObject.stringValue);
}
}
需要注意。扫码过程中可能会遇到很长的条形码。这时候可能会扫码解析很慢。解决办法一个是检查解析范围的合理性(setRectOfInterest),一个是设置相机对焦模式(setFocusMode)为连续自动对焦。再则可以将条码和二维码分开解析(设置metadataOutput.metadataObjectTypes = ??的不同解析模式)。