之前寫過文章介紹過蘋果原生CoreImage框架的使用,如何用來生成二維碼和掃描二維碼http://www.lxweimin.com/p/0884c3d83405 。 以前封裝了一個(gè)二維碼掃描和二維碼識別的界面,前幾天給稍微整理了下。文章目的主要介紹封裝思路。Demo下載地址:https://github.com/ZhengYaWei1992/ZWQRCode 具體請看效果圖。
具體如何識別相冊中的圖片功能,就不展示了,相冊中包含個(gè)人隱私??。
源碼分析:
這里我主要是創(chuàng)建了兩個(gè)類ZWQRCodeReaderView和ZWQRCodeController,前者主要負(fù)責(zé)處理二維碼掃面界面的顯示,ZWQRCodeController主要負(fù)責(zé)一些業(yè)務(wù)邏輯的處理,使用的時(shí)候只要模態(tài)彈出該視圖控制器即可,之后再實(shí)現(xiàn)如下代理方法,直接獲取掃面結(jié)果:
#pragma mark -ZWQRCodeControllerDelegate
//模仿微信一般是掃描成功后,會從當(dāng)前頁面跳轉(zhuǎn)過去,所以一般會設(shè)置一個(gè)代理
- (void)qrcodeController:(ZWQRCodeController *)qrcodeController readerScanResult:(NSString *)readerScanResult type:(ZWQRCodeResultType)resultType{
/*
ZWQRCodeResultTypeSuccess = 0, // 1.成功獲取圖片中的二維碼信息
ZWQRCodeResultTypeNoInfo = 1, // 2.識別的圖片沒有二維碼信息
ZWQRCodeResultTypeError = 2 // 3.其他錯(cuò)誤
*/
if (resultType == ZWQRCodeResultTypeSuccess) {
NSLog(@"%@",readerScanResult);
if ([readerScanResult hasPrefix:@"http"]) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:readerScanResult]];
}
}
}
關(guān)于如何掃描二維碼相關(guān)代碼不再j介紹,具體請看之前的文章,二維碼掃面使用起來很簡單。重點(diǎn)是類似這樣的界面如何搭建,尤其是中間的一塊透明區(qū)域,邊上的朦朧效果。可能有人會說是通過UI給的特殊圖片就可以實(shí)現(xiàn)的。其實(shí)并不是這樣的。主要是借助神奇的CAShapeLayer和貝塞爾的完美結(jié)合。首先ZWQRCodeReaderView這個(gè)類的實(shí)例對象中,添加一個(gè)UIView(_viewMask)并將背景色整體設(shè)置為灰蒙蒙的狀態(tài),之后再挖空中間的部分,實(shí)現(xiàn)代碼如下。注意這行代碼是關(guān)鍵:bezierPathByReversingPath
UIBezierPath *path = [UIBezierPath bezierPath];
[path appendPath:[UIBezierPath bezierPathWithRect:_viewMask.bounds]];
//有效掃描區(qū)的圖片顯示和全透明區(qū)域完全重合
//如果將.bezierPathByReversingPath則不會產(chǎn)生中間全透明部分
[path appendPath:[UIBezierPath bezierPathWithRect:self.rectScanZone].bezierPathByReversingPath];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.path = path.CGPath;
_viewMask.layer.mask = maskLayer;
關(guān)于如何布局。
如果不通過約束來布局,也不借助第三方約束框架,這樣的界面該如何布局以適配不同尺寸的設(shè)備,其實(shí)這里面也是很有技巧的。主要是借助一個(gè)比例來設(shè)置frame值,而不是具體的數(shù)值。將屏幕的寬度320段等比劃分,之后布局的話,主要借助ZW_QRCODE_WidthRate這個(gè)宏。
#define ZW_QRCODE_ScreenWidth CGRectGetWidth([UIScreen mainScreen].bounds)
#define ZW_QRCODE_WidthRate ZW_QRCODE_ScreenWidth/320
關(guān)于如何識別相冊中圖片的二維碼。識別圖片中的二維碼主要借助CIDetector這個(gè)類。
- (CIDetector *)detector
{
if (!_detector) {
_detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{ CIDetectorAccuracy : CIDetectorAccuracyHigh }];
}
return _detector;
}
從系統(tǒng)相冊中選中圖片后,主要通過 NSArray *features = [self.detector featuresInImage:[CIImage imageWithCGImage:image.CGImage]];這行代碼獲取識別信息,相關(guān)識別信息都包含在這個(gè)數(shù)組中,剩下的信息處理邏輯就和普通掃面二維碼識別邏輯一樣的。
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
// 1.獲取圖片信息
UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage];
if (!image){
image = [info objectForKey:UIImagePickerControllerOriginalImage];
}
// 2.退出圖片控制器
[picker dismissViewControllerAnimated:YES completion:^{
[[UIApplication sharedApplication]setStatusBarStyle:UIStatusBarStyleLightContent];
NSArray *features = [self.detector featuresInImage:[CIImage imageWithCGImage:image.CGImage]];
if (features.count) { // 1.識別到二維碼
// 1.播放提示音
SystemSoundID soundID;
NSString *strSoundFile = [[NSBundle zw_qrCodeBundle] pathForResource:@"noticeMusic" ofType:@"wav"];
AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL fileURLWithPath:strSoundFile],&soundID);
AudioServicesPlaySystemSound(soundID);
// 2.顯示掃描結(jié)果信息
CIQRCodeFeature *feature = [features objectAtIndex:0];
// [ZWQRCodeAlert showWithTitle:feature.messageString];
if ([self.delegate respondsToSelector:@selector(qrcodeController:readerScanResult:type:)]) {
[self.delegate qrcodeController:self readerScanResult:feature.messageString type:ZWQRCodeResultTypeSuccess];
//掃描到結(jié)果后,直接返回,不要設(shè)置動畫返回
[self dismissViewControllerAnimated:YES completion:^{}];
}
}else {
[ZWQRCodeAlert showWithTitle:@"沒有識別到二維碼信息"];
}
}];
}
關(guān)于如何控制閃光燈開關(guān)。
#pragma mark- Action
//閃光燈按鈕點(diǎn)擊事件
- (void)turnTorchEvent:(UIButton *)button{
button.selected = !button.selected;
Class captureDeviceClass = NSClassFromString(@"AVCaptureDevice");
if (captureDeviceClass != nil) {
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if ([device hasTorch] && [device hasFlash]){
[device lockForConfiguration:nil];
if (button.selected) {
[device setTorchMode:AVCaptureTorchModeOn];
[device setFlashMode:AVCaptureFlashModeOn];
} else {
[device setTorchMode:AVCaptureTorchModeOff];
[device setFlashMode:AVCaptureFlashModeOff];
}
[device unlockForConfiguration];
}
}
}