iOS 使用原生API扫描二维码

添加需要的代理


定义对象

//输入输出数据链
@property (nonatomic, strong) AVCaptureSession *session;
//是否读取数据
@property (nonatomic, assign) BOOL *readingCode;

初始化

//先判断是否获取相机权限
    AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    if(authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied){        
        UIAlertView *alert =[[UIAlertView alloc]initWithTitle:@"****" message:@"请在iPhone的“设置”-“隐私”-“相机”功能中,找到“****”打开相机访问权限" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles: nil];
        alert.delegate = self;
        [alert show];        
        return;        
    }
    
    //获取摄像头
    AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    //创建输入流
    AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:nil];
    //创建输出流
    AVCaptureMetadataOutput *captureOutput = [[AVCaptureMetadataOutput alloc] init];
    //把代理设置在主线程
    [captureOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    
    //初始化数据链
    self.session = [[AVCaptureSession alloc] init];
    [self.session setSessionPreset:AVCaptureSessionPresetHigh];//高质量采集数据
    [self.session addInput:captureInput];
    [self.session addOutput:captureOutput];
    
    //扫描边框
    UIView *borderView = [[UIView alloc] initWithFrame:CGRectMake((self.view.width - 250)/2, (self.view.height - 250 - self.myStartPosY)/2, 250, 250)];
    borderView.backgroundColor = [UIColor clearColor];
    borderView.layer.borderColor = [UIColor redColor].CGColor;
    borderView.layer.borderWidth = 2.0;
    [self.view addSubview:borderView];
    
    //设置扫码支持的二维码格式(如需要其他格式,可是自己添加)
    captureOutput.metadataObjectTypes=@[AVMetadataObjectTypeQRCode];
    //显示摄像头画面
    AVCaptureVideoPreviewLayer * captureLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
    captureLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    captureLayer.frame = CGRectMake(0, self.myStartPosY, self.view.width, self.view.height - self.myStartPosY);
    [self.view.layer insertSublayer:captureLayer atIndex:0];
    //开始捕获
    [self.session startRunning];
    self.readingCode = YES;

优化建议:给captureOutput设置扫描区域,默认是全屏扫描。
AVCaptureMetadataOutput有个属性rectOfInterest就是设置识别区域的,不过这个rect 的四个参数是比例值,介于0-1之间。
AVCaptureVideoPreviewLayer有个方法:
(但是我运行的时候返回的rect的四个值都是 0,不知道为什么,所以没用这个方法,还是自己计算的范围)

//把一个在captureLayer坐标系中的rect 转换成 一个在metadataoutputs坐标系中的rect。
- (CGRect)metadataOutputRectOfInterestForRect:(CGRect)rectInLayerCoordinates;
//实现扫描区域是(20, 200, 300, 300)(计算范围)
CGRect outputInterest = CGRectMake(20/self.view.width, 200/self.view.height, 300/self.view.width, 300/self.view.height);
captureOutput.rectOfInterest = outputInterest;

实现代理回调

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
    if (self.readingCode) {
        if (metadataObjects && [metadataObjects count] > 0) {
            AVMetadataMachineReadableCodeObject *metadataObject = [metadataObjects objectAtIndex:0];
            //输出扫描字符串
            NSLog(@"%@",metadataObject.stringValue);
            if (metadataObject.stringValue.length > 0) {
                self.readingCode = NO;
                [self.session stopRunning];
                self.session = nil;
            }
        }
    }
}

扩展

使用layer的mask制作明暗对比界面

//阴影的view
UIView *maskView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.width, self.view.height)];
maskView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
[self.view addSubview:maskView];
//设置绘制的范围
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRect:maskView.bounds];
//绘制透明的范围
[maskPath appendPath:[[UIBezierPath bezierPathWithRoundedRect:CGRectMake(20, 200, 300, 300) cornerRadius:1] bezierPathByReversingPath]];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.path = maskPath.CGPath;
maskView.layer.mask = maskLayer;

你可能感兴趣的