先上效果图:
1.
在 iOS7 以前,在iOS中实现二维码和条形码扫描,我们所知的有,两大开源组件 ZBar 与 ZXing.
AVFoundation无论在扫描灵敏度和性能上来说都是最优的,所以毫无疑问我们应该切换到AVFoundation,需要兼容IOS6或之前的版本可以用zbar或zxing代替。
2.
另一个提升扫描速度和性能的就是设置解析的范围,在zbar和zxing中就是scanCrop, AVFoundation中设置AVCaptureMetadataOutput 的 rectOfInterest 属性来配置解析范围。
#import "AVCaptureMetadataOutputViewController.h" #import <AVFoundation/AVFoundation.h> #define LINE_SCAN_TIME 3.0 // 扫描线从上到下扫描所历时间(s) @interface AVCaptureMetadataOutputViewController ()<AVCaptureMetadataOutputObjectsDelegate> { AVCaptureSession * session;//输入输出的中间桥梁 UILabel *_infoLabel; CGRect mainBounds; CGRect readerFrame; CGSize viewFinderSize; } @property (nonatomic, strong) UIImageView *scanLineImageView; @property (nonatomic, strong) NSTimer *scanLineTimer; @end @implementation AVCaptureMetadataOutputViewController #pragma mark - life Cycle - (void)viewDidLoad { [super viewDidLoad]; self.title = @"二维码扫描"; [self loadAVCapture]; } - (void)viewWillAppear:(BOOL)animated { self.navigationController.navigationBar.hidden = YES; UIApplication *application = [UIApplication sharedApplication]; [application setStatusBarHidden:YES]; [super viewWillAppear:animated]; //6.启动会话 //[self.session startRunning]; } - (void)viewDidAppear:(BOOL)animated { if (![self isCkeckAVdevice]) return; if (self.scanLineTimer == nil) { [self initScanLineView]; [self createTimer]; } [super viewDidAppear:YES]; } - (void)viewWillDisappear:(BOOL)animated { self.navigationController.navigationBar.hidden = NO; UIApplication *application = [UIApplication sharedApplication]; [application setStatusBarHidden:NO]; [super viewWillDisappear:animated]; } #pragma mark - load AVCapture - (void)loadAVCapture { mainBounds = [[UIScreen mainScreen] bounds]; self.view.frame = mainBounds; readerFrame = self.view.frame; viewFinderSize = CGSizeMake(readerFrame.size.width - 80, readerFrame.size.width - 80); if ([self isCkeckAVdevice]) { [self initAVCaptureSession]; [self initButtonView]; [self initAVBorderView]; [self initFourEdgeView]; [self initHintView]; } self.view.backgroundColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.2]; } - (BOOL)isCkeckAVdevice { AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; NSError *error = nil; AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error]; if (error) { NSLog(@"没有摄像头%@", error.localizedDescription); input = nil; UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"" message:@"没有摄像头" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil]; [alertView show]; return NO; } return YES; } - (void)initAVCaptureSession { //获取摄像设备 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()]; //初始化链接对象 session = [[AVCaptureSession alloc]init]; //高质量采集率 [session setSessionPreset:AVCaptureSessionPresetHigh]; [session addInput:input]; [session addOutput:output]; //设置扫码支持的编码格式(如下设置条形码和二维码兼容) output.metadataObjectTypes=@[AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code]; AVCaptureVideoPreviewLayer * layer = [AVCaptureVideoPreviewLayer layerWithSession:session]; layer.videoGravity=AVLayerVideoGravityResizeAspectFill; layer.frame=self.view.layer.bounds; [self.view.layer insertSublayer:layer atIndex:0]; //开始捕获 [session startRunning]; } #pragma mark - init UI View - (void)initScanLineView { self.scanLineImageView = [[UIImageView alloc] initWithFrame:CGRectMake((readerFrame.size.width - 230)/2, (readerFrame.size.height - viewFinderSize.height)/2, 230, 10)]; self.scanLineImageView.image = [UIImage imageNamed:@"qr_scan_line"]; [self.view addSubview:self.scanLineImageView]; } - (void)initHintView { _infoLabel = [[UILabel alloc] initWithFrame:CGRectMake((readerFrame.size.width - viewFinderSize.width)/2, (readerFrame.size.height + viewFinderSize.height)/2 + 20, viewFinderSize.width, 30)]; _infoLabel.text = @"将二维码放入取景框中即可自动扫描"; _infoLabel.font = [UIFont systemFontOfSize:13.0]; _infoLabel.layer.cornerRadius = _infoLabel.frame.size.height / 2; _infoLabel.layer.backgroundColor = [[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.5] CGColor]; _infoLabel.textColor = [UIColor whiteColor]; _infoLabel.textAlignment = NSTextAlignmentCenter; [self.view addSubview:_infoLabel]; } - (void)initAVBorderView { /* 画一个取景框结束 */ UIView *topLine = [[UIView alloc] initWithFrame:CGRectMake((readerFrame.size.width - viewFinderSize.width)/2-1, (readerFrame.size.height - viewFinderSize.height)/2-1, viewFinderSize.width+2, 1)]; topLine.backgroundColor = [UIColor grayColor]; [self.view addSubview:topLine]; UIView *bottomLine = [[UIView alloc] initWithFrame:CGRectMake((readerFrame.size.width - viewFinderSize.width)/2-1, (readerFrame.size.height + viewFinderSize.height)/2, viewFinderSize.width+2, 1)]; bottomLine.backgroundColor = [UIColor grayColor]; [self.view addSubview:bottomLine]; UIView *leftLine = [[UIView alloc] initWithFrame:CGRectMake((readerFrame.size.width - viewFinderSize.width)/2-1, (readerFrame.size.height - viewFinderSize.height)/2-1, 1, viewFinderSize.height+2)]; leftLine.backgroundColor = [UIColor grayColor]; [self.view addSubview:leftLine]; UIView *rightLine = [[UIView alloc] initWithFrame:CGRectMake((readerFrame.size.width + viewFinderSize.width)/2, (readerFrame.size.height - viewFinderSize.height)/2-1, 1, viewFinderSize.height+2)]; rightLine.backgroundColor = [UIColor grayColor]; [self.view addSubview:rightLine]; } - (void)initFourEdgeView { /* 画一个取景框开始 */ // 正方形取景框的边长 CGFloat edgeLength = 20.0; UIImageView *topLeft = [[UIImageView alloc] initWithFrame:CGRectMake((readerFrame.size.width - viewFinderSize.width)/2, (readerFrame.size.height - viewFinderSize.height)/2, edgeLength, edgeLength)]; topLeft.image = [UIImage imageNamed:@"qr_top_left.png"]; [self.view addSubview:topLeft]; UIImageView *topRight = [[UIImageView alloc] initWithFrame:CGRectMake((readerFrame.size.width + viewFinderSize.width)/2 - edgeLength, (readerFrame.size.height - viewFinderSize.height)/2, edgeLength, edgeLength)]; topRight.image = [UIImage imageNamed:@"qr_top_right.png"]; [self.view addSubview:topRight]; UIImageView *bottomLeft = [[UIImageView alloc] initWithFrame:CGRectMake((readerFrame.size.width - viewFinderSize.width)/2, (readerFrame.size.height + viewFinderSize.height)/2 - edgeLength, edgeLength, edgeLength)]; bottomLeft.image = [UIImage imageNamed:@"qr_bottom_left"]; [self.view addSubview:bottomLeft]; UIImageView *bottomRight = [[UIImageView alloc] initWithFrame:CGRectMake((readerFrame.size.width + viewFinderSize.width)/2 - edgeLength, (readerFrame.size.height + viewFinderSize.height)/2 - edgeLength, edgeLength, edgeLength)]; bottomRight.image = [UIImage imageNamed:@"qr_bottom_right"]; [self.view addSubview:bottomRight]; UIView *topLine = [[UIView alloc] initWithFrame:CGRectMake((readerFrame.size.width - viewFinderSize.width)/2-1, (readerFrame.size.height - viewFinderSize.height)/2-1, viewFinderSize.width+2, 1)]; topLine.backgroundColor = [UIColor grayColor]; [self.view addSubview:topLine]; UIView *bottomLine = [[UIView alloc] initWithFrame:CGRectMake((readerFrame.size.width - viewFinderSize.width)/2-1, (readerFrame.size.height + viewFinderSize.height)/2, viewFinderSize.width+2, 1)]; bottomLine.backgroundColor = [UIColor grayColor]; [self.view addSubview:bottomLine]; UIView *leftLine = [[UIView alloc] initWithFrame:CGRectMake((readerFrame.size.width - viewFinderSize.width)/2-1, (readerFrame.size.height - viewFinderSize.height)/2-1, 1, viewFinderSize.height+2)]; leftLine.backgroundColor = [UIColor grayColor]; [self.view addSubview:leftLine]; UIView *rightLine = [[UIView alloc] initWithFrame:CGRectMake((readerFrame.size.width + viewFinderSize.width)/2, (readerFrame.size.height - viewFinderSize.height)/2-1, 1, viewFinderSize.height+2)]; rightLine.backgroundColor = [UIColor grayColor]; [self.view addSubview:rightLine]; } - (void)initButtonView { // 返回键 UIButton *goBackButton = ({ UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(20, 30, 36, 36)]; [button setImage:[UIImage imageNamed:@"qr_vc_left"] forState:UIControlStateNormal]; button.layer.cornerRadius = 18.0; button.layer.backgroundColor = [[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.5] CGColor]; [button addTarget:self action:@selector(goBack:) forControlEvents:UIControlEventTouchDown]; button; }); [self.view addSubview:goBackButton]; // 控制散光灯 UIButton *torchSwitch = ({ UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(mainBounds.size.width-44-20, 30, 36, 36)]; [button setImage:[UIImage imageNamed:@"qr_vc_right"] forState:UIControlStateNormal]; button.layer.cornerRadius = 18.0; button.layer.backgroundColor = [[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.5] CGColor]; [button addTarget:self action:@selector(torchSwitch:) forControlEvents:UIControlEventTouchDown]; button; }); [self.view addSubview:torchSwitch]; } - (void)createTimer { self.scanLineTimer = [NSTimer scheduledTimerWithTimeInterval:LINE_SCAN_TIME target:self selector:@selector(moveUpAndDownLine) userInfo:nil repeats:YES]; } // 扫描条上下滚动 - (void)moveUpAndDownLine { CGRect scanLineframe = self.scanLineImageView.frame; scanLineframe.origin.y = (readerFrame.size.height - viewFinderSize.height)/2; self.scanLineImageView.frame = scanLineframe; self.scanLineImageView.hidden = NO; __weak __typeof(self) weakSelf = self; [UIView animateWithDuration:LINE_SCAN_TIME - 0.05 animations:^{ CGRect scanLineframe = weakSelf.scanLineImageView.frame; scanLineframe.origin.y = (readerFrame.size.height + viewFinderSize.height)/2 - weakSelf.scanLineImageView.frame.size.height; weakSelf.scanLineImageView.frame = scanLineframe; } completion:^(BOOL finished) { weakSelf.scanLineImageView.hidden = YES; }]; } #pragma mark - clicked method // 返回 - (void)goBack:(id)sender { [self.navigationController popViewControllerAnimated:YES]; } // 控制散光灯 - (void)torchSwitch:(id)sender { AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; NSError *error; if (device.hasTorch) { // 判断设备是否有散光灯 BOOL b = [device lockForConfiguration:&error]; if (!b) { if (error) { NSLog(@"lock torch configuration error:%@", error.localizedDescription); } return; } device.torchMode = (device.torchMode == AVCaptureTorchModeOff ? AVCaptureTorchModeOn : AVCaptureTorchModeOff); [device unlockForConfiguration]; } } #pragma mark - AVCaptureMetadataOutputObjectsDelegate - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection { if (metadataObjects.count>0) { //[session stopRunning]; AVMetadataMachineReadableCodeObject * metadataObject = [metadataObjects objectAtIndex : 0 ]; //输出扫描字符串 NSLog(@"%@",metadataObject.stringValue); [[UIApplication sharedApplication] openURL:[NSURL URLWithString:metadataObject.stringValue]]; //[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://itunes.apple.com/cn/app/quan-mink-ge/id910513149?mt=8"]]; } } @end