二維碼掃描相關(guān)
在開始之前最重要的一步是要先導(dǎo)入原生的音視頻框架即AVFoundation.framework
乍丈。導(dǎo)入之后直接進(jìn)入正題剧包。我封裝了一個非常簡單的二維碼(或條形碼)掃描的ViewController
趁俊,具有最基礎(chǔ)的界面来破,可以自己定制一些掃描效果等闻坚,項目地址(ps:覺得好用歡迎star)
判斷設(shè)備是否具有調(diào)用攝像頭的權(quán)限
在開始進(jìn)行掃描的時候棚辽,最好要先判斷一下App是否具有調(diào)用攝像頭的權(quán)限。如果沒有的話就不要進(jìn)行攝像頭的調(diào)用的操作了局劲,直接提示就行了勺拣,下面直接上代碼:
AVAuthorizationStatus authorizationStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
//這個語句是查看媒體設(shè)備的授權(quán)狀態(tài):AVMediaTypeVideo 表示攝像頭使用的授權(quán)狀態(tài)
/*
這個狀態(tài)是個枚舉:
AVAuthorizationStatusRestricted 表示受到限制
AVAuthorizationStatusDenied 表示拒絕訪問
AVAuthorizationStatusNotDetermined 表示用戶沒有明確選擇是否能夠問硬件
AVAuthorizationStatusAuthorized 表示已經(jīng)授權(quán)訪問
*/
//當(dāng)受到限制或拒絕訪問的時候需要用戶去開啟訪問權(quán)限
if (authorizationStatus == AVAuthorizationStatusRestricted ||
authorizationStatus == AVAuthorizationStatusDenied) {
//開啟訪問權(quán)限
}
判斷具有訪問攝像頭權(quán)限之后就能正式開始掃描二維碼了
同理直接上代碼(代碼里都有注釋...應(yīng)該都很好明白的):
NSError * error;
//設(shè)置設(shè)備
AVCaptureDevice * captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];//設(shè)置媒體類型AVMediaTypeVideo:視頻類型;AVMediaTypeAudio:音頻類型鱼填;AVMediaTypeMuxed:混合類型
//設(shè)置獲取設(shè)備輸入
AVCaptureDeviceInput * deviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:captureDevice error:&error];
if (!deviceInput) {//如果無法獲取設(shè)備輸入
NSLog(@"%@",error.localizedDescription);
return ;
}
//設(shè)置設(shè)備輸出
AVCaptureMetadataOutput * captureMetadataOutput = [[AVCaptureMetadataOutput alloc] init];
//設(shè)置捕獲會話
_captureSession = [[AVCaptureSession alloc] init];
[_captureSession addInput:deviceInput];//設(shè)置設(shè)備輸入
[_captureSession addOutput:captureMetadataOutput];//設(shè)置設(shè)備輸出
//設(shè)置輸出代理
dispatch_queue_t dispatchQueue = dispatch_queue_create("myQueue", NULL);
[captureMetadataOutput setMetadataObjectsDelegate:self queue:dispatchQueue];
//設(shè)置解析數(shù)據(jù)類型 自行在這里添加需要識別的各種碼
[captureMetadataOutput setMetadataObjectTypes:@[AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code, AVMetadataObjectTypeQRCode,AVMetadataObjectTypeUPCECode]];
//設(shè)置展示layer
_captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
[_captureVideoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
_captureVideoPreviewLayer.frame = CGRectMake(0, 0, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame));
[self.view.layer addSublayer:_captureVideoPreviewLayer];
//放大1.5倍
_captureVideoPreviewLayer.affineTransform = CGAffineTransformMakeScale(1.5, 1.5);
AVCaptureOutput * output = (AVCaptureOutput *)_captureSession.outputs[0];
AVCaptureConnection * focus = [output connectionWithMediaType:AVMediaTypeVideo];//獲得攝像頭焦點(diǎn)
focus.videoScaleAndCropFactor = 1.5;//焦點(diǎn)放大
//開始執(zhí)行攝像頭
[_captureSession startRunning];
怎樣設(shè)置rectOfInterest
設(shè)置rectOfInterest
就是設(shè)置掃描的識別范圍药有,在網(wǎng)上有很多試出來的規(guī)律,
但是這個rectOfInterest
的設(shè)置有其他解決方法,解決方法stackoverflow愤惰。
使用AVCaptureVideoPreviewLayer
中的metadataOutputRectOfInterestForRect:
方法苇经,可以讓標(biāo)準(zhǔn)坐標(biāo)系轉(zhuǎn)換為metadataOutput
的坐標(biāo)系。
具體的方法就是宦言,先添加一個通知:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationHandle:) name:AVCaptureInputPortFormatDescriptionDidChangeNotification object:nil];
然后在通知方法里面進(jìn)行處理:
- (void)notificationHandle:(NSNotification *)notification{
AVCaptureMetadataOutput * output = (AVCaptureMetadataOutput*)_captureSession.outputs[0];//從會話里面獲取設(shè)備輸出對象
CGRect rect = CGRectMake(0, 0, 100, 100);//設(shè)置掃描范圍
output.rectOfInterest = [_videoPreviewLayer metadataOutputRectOfInterestForRect:rect];//轉(zhuǎn)換掃描范圍
}
掃描界面的繪制
其實(shí)很簡單方便扇单,直接使用貝塞爾曲線繪制就行了。繪制的代碼如下:
UIView * maskView = [[UIView alloc] init];
maskView.frame = CGRectMake(0, 0, CGRectGetWidth([UIScreen mainScreen].bounds), CGRectGetHeight([UIScreen mainScreen].bounds));
maskView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.7];
[self.view addSubview:maskView];
//創(chuàng)建路徑
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, CGRectGetWidth(maskView.frame), CGRectGetHeight(maskView.frame))];//繪制和透明黑色遮蓋層一樣的矩形
//路徑取反
[path appendPath:[[UIBezierPath bezierPathWithRect:CGRectMake((CGRectGetWidth(self.view.frame)-200)/2.0, (CGRectGetHeight(self.view.frame)-200)/2.0, 200, 200)] bezierPathByReversingPath]];//繪制中間空白透明的矩形奠旺,并且取反路徑蜘澜。這樣整個繪制的范圍就只剩下中間的矩形和邊界之間的部分
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = path.CGPath;//將路徑交給layer繪制
[maskView.layer setMask:shapeLayer];//設(shè)置遮罩層