美文网首页
OC实现二维码扫描

OC实现二维码扫描

作者: 大鹏鸟 | 来源:发表于2018-03-16 17:14 被阅读202次

    对于写代码,我一向是只要时间允许,能自己写的绝对不用第三方的。因为总是用第三方的话,会给工程带来很大的不稳定性和很多坑。嗯,我现在就身在其中。
    二维码扫描,这是一个很普遍的功能,在我负责的app里也有,只不过那是个第三方的。乘着刚刚用Swift搭完架子,现在决定尝试对其封装一下,然后应用到工程中。
    二维码扫描使用的是AVFoundation框架 ,主要涉及的类有:

    • AVCaptureDevice
    • AVCaptureDeviceInput
    • AVCaptureMetadataOutput
    • AVCaptureVideoPreviewLayer
    • AVCaptureSession
      要说应用,肯定很简单,只不过这里面的坑太多太多了。

    简单的流程,总结如下:

    • 1.使用方法[AVCaptureDevice authorizationStatusForMediaType:mediaType];检查权限,但是这里的type类型只能是AVMediaTypeVideo或者AVMediaTypeAudio
    • 2.使用方法+ (void)requestAccessForMediaType:(AVMediaType)mediaType completionHandler:(void (^)(BOOL granted))handler获取权限,这里需要注意的是回调不一定就是在主线程
    • 3.创建device对象
    • 4.使用device对象创建AVCaptureDeviceInput对象
    • 5.初始化AVCaptureMetadataOutput对象和设置代理
    • 6.初始化AVCaptureSession任务管理对象
    • 7.使用如下代码添加输入输出:
    if([session canAddInput:deviceInput]) {
            [session addInput:deviceInput];
        }
        if([session canAddOutput:output]) {
            [session addOutput:output];
        }
    
    • 8.设置output对象的属性metadataObjectTypes值为AVMetadataObjectTypeQRCode
    • 9.使用session初始化AVCaptureVideoPreviewLayer,该layer就是背景透明的地方
    • 10.使用session调用方法startRunning启动扫描
    • 11.调用layer的- (CGRect)metadataOutputRectOfInterestForRect:(CGRect)rectInLayerCoordinates方法获取output的rectOfInterest,这里的rectInLayerCoordinates参数就是扫描框的位置,即识别区。注意,这里必须要写到startRunning调用之后。
    • 12.调用layer的rectForMetadataOutputRectOfInterest方法获取扫描框的位置
    • 13.获取结果

    以上,这是一个比较好理解和使用的思路,这里的rectOfInterest其实也可以自己计算获取到,但是比较难掌握,因为它和AVCaptureVideoPreviewLayer的videoGravity有关,也和session的sessionPreset有关,具体的待我稍作研究补上。
    具体的代码如下:

    - (void)configUI {
        
        AVMediaType mediaType = AVMediaTypeVideo;
        // 先检查权限,如果没有权限,就去申请获取
        AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:mediaType];
        switch (status) {
            case AVAuthorizationStatusNotDetermined:{
                [AVCaptureDevice requestAccessForMediaType:mediaType completionHandler:^(BOOL granted) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        if (granted) {
                            [self continueConfigUI:mediaType];
                        } else {
                            NSLog(@"denied");
                        }
                    });
                }];
            }
                break;
            case AVAuthorizationStatusAuthorized:{
                [self continueConfigUI:mediaType];
            }
                break;
                
            default:
                NSLog(@"denied");
                break;
        }
    }
    
    - (void)continueConfigUI:(AVMediaType)mediaType {
        AVCaptureDevice * device = [AVCaptureDevice defaultDeviceWithMediaType:mediaType];
        if (!device) {
            return;
        }
    
        CGRect cameraFrame = CGRectMake(10, 100, 300, 300);
        CGRect scanRect = CGRectMake(cameraFrame.size.width / 2 - 100, cameraFrame.size.height / 2 - 100, 200, 200);
        
        AVCaptureDeviceInput * deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
        output = [[AVCaptureMetadataOutput alloc] init];
        [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
        
    //    CGFloat top = scanRect.origin.y / CGRectGetHeight(cameraFrame);
    //    CGFloat left = scanRect.origin.x / CGRectGetWidth(cameraFrame);
    //    CGFloat width = scanRect.size.width / CGRectGetWidth(cameraFrame);
    //    CGFloat height = scanRect.size.height / CGRectGetHeight(cameraFrame);
    //    output.rectOfInterest = CGRectMake(top, left, height, width);
        
        session = [[AVCaptureSession alloc] init];
        if([session canAddInput:deviceInput]) {
            [session addInput:deviceInput];
        }
        if([session canAddOutput:output]) {
            [session addOutput:output];
        }
        output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode]; // 必须放在上面设置完session之后
        
        _cameraView = [[UIView alloc] initWithFrame:cameraFrame];
        _cameraView.backgroundColor = [UIColor whiteColor];
        [self.view addSubview:_cameraView];
        
        // 添加扫描画面
        AVCaptureVideoPreviewLayer * layer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
        layer.videoGravity = AVLayerVideoGravityResizeAspectFill; // 该值会影响展示
        layer.frame = _cameraView.bounds;
        [_cameraView.layer addSublayer:layer];
    //     output.rectOfInterest = [layer metadataOutputRectOfInterestForRect:scanRect];
        [session startRunning];
        
        output.rectOfInterest = [layer metadataOutputRectOfInterestForRect:scanRect];  // 采用此方法必须放到startRunning之后
        
        [self.view bringSubviewToFront:_backButton];
        [self.view bringSubviewToFront:_startButton];
        
        _middleView = [[UIView alloc] initWithFrame:[layer rectForMetadataOutputRectOfInterest:output.rectOfInterest]];
        _middleView.backgroundColor = [UIColor clearColor];
        _middleView.layer.borderWidth = 5;
        _middleView.layer.borderColor = [UIColor orangeColor].CGColor;
        [_cameraView addSubview:_middleView];
       
    //    UIView * recView = [[UIView alloc] initWithFrame:scanRect];
    //    recView.backgroundColor = [UIColor clearColor];
    //    recView.layer.borderWidth = 2;
    //    recView.layer.borderColor = [UIColor greenColor].CGColor;
    //    [_cameraView addSubview:recView];
    }
    
    - (void)captureOutput:(AVCaptureOutput *)output didOutputMetadataObjects:(NSArray<__kindof AVMetadataObject *> *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
        if(metadataObjects.count > 0) {
            [session stopRunning];
            AVMetadataMachineReadableCodeObject * readCode = metadataObjects.firstObject;
            UIAlertController * alertVC = [UIAlertController alertControllerWithTitle:@"结果" message:readCode.stringValue preferredStyle:UIAlertControllerStyleAlert];
            UIAlertAction * alertAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleDefault handler:nil];
            [alertVC addAction:alertAction];
            [self presentViewController:alertVC animated:YES completion:nil];
            NSLog(@"%@", readCode.stringValue);
        }
    }
    

    代码在这里

    相关文章

      网友评论

          本文标题:OC实现二维码扫描

          本文链接:https://www.haomeiwen.com/subject/yehrqftx.html