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 移除動畫,並把掃描框從當前檢視中移除