1. 程式人生 > >iOS 學習日誌 : iOS原生二維碼的掃描以及限定掃描範圍

iOS 學習日誌 : iOS原生二維碼的掃描以及限定掃描範圍

現在的app多少都會加入二維碼掃描功能,方便快捷,開發中常常會碰到這樣的需求.

定義會話和輸出流物件

@property (nonatomic) AVCaptureSession *captureSession;
@property (nonatomic) AVCaptureVideoPreviewLayer *videoPreviewLayer;
// 獲取 AVCaptureDevice 例項
    NSError * error;
    AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    // 初始化輸入流
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error]; if (!input) { NSLog(@"%@", [error localizedDescription]); return NO; } // 建立會話 _captureSession = [[AVCaptureSession alloc] init]; // 新增輸入流 [_captureSession addInput:input]; // 初始化輸出流
AVCaptureMetadataOutput *captureMetadataOutput = [[AVCaptureMetadataOutput alloc] init]; // 新增輸出流 [_captureSession addOutput:captureMetadataOutput]; // 建立dispatch queue. dispatch_queue_t dispatchQueue; dispatchQueue = dispatch_queue_create(kScanQRCodeQueueName, NULL); [captureMetadataOutput setMetadataObjectsDelegate:self
queue:dispatchQueue]; // 設定資料型別 AVMetadataObjectTypeQRCode [captureMetadataOutput setMetadataObjectTypes:[NSArray arrayWithObject:AVMetadataObjectTypeQRCode]]; // 建立輸出物件 _videoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession]; [_videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill]; [_videoPreviewLayer setFrame:self.view.bounds]; [self.view.layer addSublayer:_videoPreviewLayer]; // 開始會話 [_captureSession startRunning];

這裡準備工作就做完了,在startRunning後就會開啟二維碼的掃描,然後遵守AVCaptureMetadataOutputObjectsDelegate代理 ,在代理方法中處理掃碼獲得的資料

-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects
      fromConnection:(AVCaptureConnection *)connection
{
    if (metadataObjects != nil && [metadataObjects count] > 0) {
        AVMetadataMachineReadableCodeObject *metadataObj = [metadataObjects objectAtIndex:0];
        NSString *result;
        [_captureSession stopRunning];
        if ([[metadataObj type] isEqualToString:AVMetadataObjectTypeQRCode]) {
            result = metadataObj.stringValue;
        } else {
            NSLog(@"不是二維碼");
        }

    }
}

這樣最基礎的二維碼掃描就做完了,但是使用者體驗並不好,因為預設的是全屏掃描,而且原生的二維碼掃描很快,容易誤操作,影響使用者體驗.

所以我們需要限定掃描的範圍,AVCaptureMetadataOutput 有對應的屬性設定掃描範圍,但是它的座標系有些特殊 並不是常見的(x,y,width,height)而是(y,x,height,width),所以就需要將座標翻轉過來,下面是一個左右間距各50,居中的正方形掃描框,那麼就將掃描範圍限定在這個掃描框裡。
AVCaptureMetadataOutput *captureMetadataOutput = [[AVCaptureMetadataOutput alloc] init];
[captureMetadataOutput setRectOfInterest:CGRectMake(([UIScreen mainScreen].bounds.size.height/2-([UIScreen mainScreen].bounds.size.width-100)/2)/
[UIScreen mainScreen].bounds.size.height,50/[UIScreen mainScreen].bounds.size.width, ([UIScreen mainScreen].bounds.size.width-100)/[UIScreen mainScreen].bounds.size.height, ([UIScreen mainScreen].bounds.size.width-100)/[UIScreen mainScreen].bounds.size.width)];

最後嘛 可以利用view的動畫來完成掃描框的線性”掃描”,提升使用者體驗

下面是利用masory約束的一個 左右間距50並居中的掃描框,然後利用動畫完成線性的掃描,欺騙使用者…….0.0

UIView * anniView= [[UIView alloc]init];
    anniView.tag = 111;
    [self.view addSubview:anniView];
    [anniView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.equalTo(self.view.mas_centerX);
        make.centerY.equalTo(self.view.mas_centerY);
        make.left.equalTo(self.view).with.offset(50);
        make.right.equalTo(self.view).with.offset(-50);
        make.width.equalTo(anniView.mas_height);
    }];
    //掃描框
    UIImageView * imageView = [[UIImageView alloc]init];
    [anniView addSubview:imageView];
    [imageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(anniView).insets(UIEdgeInsetsMake(0, 0, 0, 0));
    }];
    imageView.image = [UIImage imageNamed:@"cornround"];
    //掃描線
    UIImageView * line = [[UIImageView alloc]init];
    [anniView insertSubview:line aboveSubview:imageView];
    line.backgroundColor = [UIColor clearColor];
    line.image = [UIImage imageNamed:@"line"];
    [line mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(anniView).with.offset(0);
        make.right.equalTo(anniView).with.offset(0);
        make.top.equalTo(anniView).with.offset(0);
        make.height.equalTo(@2);
    }];
    CABasicAnimation *animationMove = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
    [animationMove setFromValue:@(0)];
    [animationMove setToValue:@([UIScreen mainScreen].bounds.size.width-102)];
    animationMove.duration = 5.0f;
    animationMove.delegate = self;
    animationMove.repeatCount  = OPEN_MAX;
    animationMove.removedOnCompletion = NO;
    animationMove.fillMode = kCAFillModeForwards;
    animationMove.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    [line.layer addAnimation:animationMove forKey:animationKey];

最後在掃碼獲取到資料之後,停止掃描.並根據tag值獲取UIImageView 移除動畫,並把掃描框從當前檢視中移除