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

先说扫描的 ios7&later

引入类库

#import 

签协议

@interface NAHomeBarcodeViewController ()

声明属性

@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实现二维码识别, 扫描及添加灰色遮罩)