美文网首页
原生API实现二维码识别, 扫描及添加灰色遮罩

原生API实现二维码识别, 扫描及添加灰色遮罩

作者: NateLam | 来源:发表于2016-08-25 10:39 被阅读346次

    先说扫描的 ios7&later

    引入类库

    #import <AVFoundation/AVFoundation.h>
    

    签协议

    @interface NAHomeBarcodeViewController ()<AVCaptureMetadataOutputObjectsDelegate>
    

    声明属性

    @property (nonatomic, strong) AVCaptureSession *session;//输入输出的中间桥梁
    @property (strong,nonatomic)AVCaptureDevice *device;
    @property (strong,nonatomic)AVCaptureDeviceInput *input;
    @property (strong,nonatomic)AVCaptureMetadataOutput *output;
    @property (strong,nonatomic)AVCaptureVideoPreviewLayer * previewLayer;
    
    @property (nonatomic, strong) CADisplayLink *link;//定时器
    @property (nonatomic, strong) CALayer *scanLayer;//扫描线
    @property (nonatomic, strong) UIView *boxView;//扫描框
    @property (nonatomic, strong) NSString *strOfResult;//保存二维码结果
    
    //在扫描框上下左右的灰色透明遮罩
    @property (nonatomic, strong) UIView *topV;
    @property (nonatomic, strong) UIView *leftV;
    @property (nonatomic, strong) UIView *rightV;
    @property (nonatomic, strong) UIView *bottomV;
    

    添加定时器, 让扫描线上下滚动

    #pragma mark - 创建定时器
    - (void)createTimer{
    
        _link = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateFrame)];
        _link.frameInterval = 1.5;
        [_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
    }
    
    #pragma mark - 上下刷
    - (void)updateFrame{
    
        CGRect frame = self.scanLayer.frame;
        if (self.scanLayer.frame.origin.y > self.boxView.frame.size.height) {
            frame.origin.y = -50;
            self.scanLayer.frame = frame;
        }else{
           frame.origin.y += 1;
            self.scanLayer.frame = frame;
        }
    }
    

    然后就是添加扫描相关事项了

    #pragma mark - 扫描
    - (void)scanCode{
    
        //1.获取输入设备
        _device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    
        //创建输入对象
        NSError *error;
        _input = [AVCaptureDeviceInput deviceInputWithDevice:_device error:&error];
    
        if (!_input) {
            [self alertNotOpenCamera];
        }
    
        //3.创建输出对象
        _output = [[AVCaptureMetadataOutput alloc] init];
    
        //4.设置代理监听输出对象的输出流  说明: 使用主线程队列,  相应比较同步, 使用其他队列,  相应不同步,  容易让用户产生不好的体验
        [self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    
        //5.创建会话
        _session = [[AVCaptureSession alloc] init];
    
        //高质量采集率
        [self.session setSessionPreset:AVCaptureSessionPresetHigh];
    
        //6.将输入输出对象添加到会话
        [self.session addInput:_input];
        [self.session addOutput:self.output];
    
        //7.告诉输出对象, 需要输出什么样的数据 提示:  一定要先设置会话的输出为output之后,  再指定输出的源数据类型!
        //设置扫码支持的编码格式(如下设置条形码和二维码兼容)
        [self.output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code]];
    
        //8. 创建预览图层
    _previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
    
        _previewLayer.frame = self.view.bounds;
        [self.view.layer insertSublayer:_previewLayer atIndex:0];
    
        //9.设置扫描范围
    //    self.output.rectOfInterest = CGRectMake(0.2, 0.18, 0.6, 0.5);
    
        CGSize size = self.view.bounds.size;
        CGRect cropRect = CGRectMake(0.2 * SCREEN_WIDTH, 0.18 * SCREEN_HEIGHT, 0.6 * SCREEN_WIDTH, 0.5 * SCREEN_HEIGHT);
    
        CGFloat p1 = size.height/size.width;
        CGFloat p2 = 1920./1080.;  //使用了1080p的图像输出
        if (p1 < p2) {
            CGFloat fixHeight = self.view.bounds.size.width * 1920. / 1080.;
            CGFloat fixPadding = (fixHeight - size.height)/2;
            self.output.rectOfInterest = CGRectMake((cropRect.origin.y + fixPadding)/fixHeight,
                                           cropRect.origin.x/size.width,
                                           cropRect.size.height/fixHeight,
                                           cropRect.size.width/size.width);
        } else {
            CGFloat fixWidth = self.view.bounds.size.height * 1080. / 1920.;
            CGFloat fixPadding = (fixWidth - size.width)/2;
            self.output.rectOfInterest = CGRectMake(cropRect.origin.y/size.height,
                                           (cropRect.origin.x + fixPadding)/fixWidth,
                                           cropRect.size.height/size.height,
                                           cropRect.size.width/fixWidth);
        }
    
        //10.设置扫描框
         [self.view addSubview:self.boxView];
    
        //设置扫描线
        [self.boxView.layer addSublayer:self.scanLayer];
    
        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
        
            if (granted) {
                NSLog(@"允许扫描");
            
                //开始扫描
                [self.session startRunning];
            }else{
                NSLog(@"不允许");
                return ;
            }
        }];
    
    
        //判断是否授权相机
        AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
        if (status == AVAuthorizationStatusDenied) {
            [self alertNotOpenCamera];
        }
    }
    

    创建定时器和创建扫描都是视图即将出现的时候调用的

    以下是几个懒加载, 扫描框还是写好大小然后和设计师要张图比较漂亮些

    - (CALayer *)scanLayer{
    
        if (!_scanLayer) {
            _scanLayer = [[CALayer alloc] init];
            _scanLayer.frame = CGRectMake(0, 0, self.boxView.bounds.size.width, 1);
            _scanLayer.backgroundColor = [UIColor blueColor].CGColor;
    //        [self.boxView.layer addSublayer:_scanLayer];
        }
        return _scanLayer;
    }
    
    //扫描框
    - (UIView *)boxView{
    
        if (!_boxView) {
             _boxView = [[UIView alloc] initWithFrame:CGRectMake(0.2 * SCREEN_WIDTH, 0.18 * SCREEN_HEIGHT, 0.6 * SCREEN_WIDTH, 0.5 * SCREEN_HEIGHT)];
        _boxView = [[UIImageView alloc] initWithFrame:CGRectMake(0.15 * SCREEN_WIDTH, 0.18 * SCREEN_HEIGHT, 0.7 * SCREEN_WIDTH, 0.7 * SCREEN_WIDTH)];
        
        [_boxView setImage:[UIImage imageNamed:@"box"]];
        
    //        _boxView.layer.borderColor = [UIColor colorWithRed:239 / 255.f green:239 / 255.f blue:239 / 255.f alpha:1.f].CGColor;
    //        _boxView.layer.borderColor = kRGBColor(38, 64, 255).CGColor;
    //        _boxView.layer.borderWidth = 0.5;
        [self createGrayBackView];
        
        //        [self.view addSubview:_boxView];
        }
        return _boxView;
    }
    
    - (void)createGrayBackView{
    
       CGRect topVFrame = CGRectMake(0, 0, SCREEN_WIDTH, 0.18 * SCREEN_HEIGHT);
    
    CGRect leftVFrame = CGRectMake(0, topVFrame.origin.y + topVFrame.size.height, 0.15 * SCREEN_WIDTH, 0.7 * SCREEN_WIDTH);
    
    CGRect bottomVFrame = CGRectMake(0, leftVFrame.origin.y +leftVFrame.size.height, SCREEN_WIDTH, SCREEN_HEIGHT - leftVFrame.origin.y - leftVFrame.size.height);
    
    CGRect rightVFrame = CGRectMake(0.15 * SCREEN_WIDTH + 0.7 * SCREEN_WIDTH, leftVFrame.origin.y, leftVFrame.size.width, leftVFrame.size.height);
    
    self.topV = [[UIView alloc] initWithFrame:topVFrame];
    [self.topV setBackgroundColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:0.3]];
    
    self.leftV = [[UIView alloc] initWithFrame:leftVFrame];
    [self.leftV setBackgroundColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:0.3]];
    
    self.bottomV = [[UIView alloc] initWithFrame:bottomVFrame];
    [self.bottomV setBackgroundColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:0.3]];
    
    self.rightV = [[UIView alloc] initWithFrame:rightVFrame];
    [self.rightV setBackgroundColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:0.3]];
    
    [self.view addSubview:self.topV];
    [self.view addSubview:self.leftV];
    [self.view addSubview:self.bottomV];
    [self.view addSubview:self.rightV];
    }
    

    本VC的生命周期做的几件事

    #pragma mark - 视图即将出现
    - (void)viewWillAppear:(BOOL)animated{
    
        [super viewWillAppear:animated];
        [self createTimer];
        [self scanCode];
    }
    
    #pragma mark - 视图已经消失
    - (void)viewDidDisappear:(BOOL)animated{
    
        [super viewDidDisappear:animated];
    //    记得释放CADisplayLink对象
        if(_link != nil){
        
            [_link invalidate];
            _link = nil;
        }
    
        //回来相机才不会卡住
        [_previewLayer removeFromSuperlayer];
    }
    

    以下是从相册识别二维码, ios8&later
    主要是CIDetector
    感谢http://ios.jobbole.com/84730/

    首先, 签俩代理

    @interface NAScanViewController ()< UIImagePickerControllerDelegate, UINavigationControllerDelegate>
    

    写个属性

    @property (nonatomic, strong) UIImagePickerController *imagePicker;
    

    在按钮响应事件里呼出相册, 代理置空这种事就不说了, 自己自觉

    #pragma mark - navi右按钮响应事件
    - (void)goToAlbum:(UIBarButtonItem *)btn{
    
        //调用相册
        self.imagePicker = [[UIImagePickerController alloc]init];
        self.imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
        self.imagePicker.delegate = self;
        //模态推出照相机页面的样式
        self.imagePicker.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
        self.imagePicker.allowsEditing = NO;
        [self presentViewController:self.imagePicker animated:YES completion:nil];
    }
    

    imagePicker的代理回调

    #pragma mark - 从相册选完图片的回调
    -(void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
    
        NSString *content = [NSString string];
        //取出选中的图片
        UIImage *pickImage = info[UIImagePickerControllerOriginalImage];
    
        NSData *imageData = [NSData data];
        if(UIImagePNGRepresentation(pickImage)){
            imageData = UIImagePNGRepresentation(pickImage);
        }else if (UIImageJPEGRepresentation(pickImage, 1)){
            imageData = UIImageJPEGRepresentation(pickImage, 1);
        }
    
        CIImage *ciImage = [CIImage imageWithData:imageData];
    
        //创建探测器
        CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}];
        NSArray *feature = [detector featuresInImage:ciImage];
    
        //取出探测到的数据
        for (CIQRCodeFeature *result in feature) {
            content = result.messageString;
        }
    
        [picker dismissViewControllerAnimated:YES completion:nil];
    
        if(!kStringIsEmpty(content)){
     
            [self jumpToWebviewWithUrl:content];
        }
    }
    

    跳转出去时给webView传识别的内容, 一般是url, 然后扫描界面停止扫描, 移除CADisplayLink对象

    pragma mark 识别成功的相关操作

    - (void)jumpToWebviewWithUrl:(NSString *)url{
    
        self.webViewVc.strOfUrl = url;
    
        //停止扫描
        [self.session stopRunning];
    
        //移除CADisplayLink对象
        [_link removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
        _link = nil;
    
        [self.navigationController pushViewController:self.webViewVc animated:YES];
    }
    

    相关文章

      网友评论

          本文标题:原生API实现二维码识别, 扫描及添加灰色遮罩

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