在 iOS7 之前,在 iOS 中實現二維碼和條形碼掃描,咱們所知的有,兩大開源組件 ZBar 與 ZXing。iOS7 以後能夠利用系統原生 API 生成二維碼, iOS8 以後能夠生成條形碼, 系統默認生成的顏色是黑色。java
在 iOS8 + 系統中使用相機須要在 Info.plist 中添加 Privacy - Camera Usage Description,並設置其值。使用相冊須要在 Info.plist 中添加 Privacy - Photo Library Usage Description,並設置其值。c++
按照下圖在 Info.plist 文件中將 Localization native development region 的值改成 China。若是不設置此項彈出的相冊頁面中顯示的按鈕等爲英文菜單。git
官方提供的接口很是簡單,直接看代碼,主要使用的是 AVFoundation。github
// 包含頭文件 #import <AVFoundation/AVFoundation.h> // 遵照協議 <AVCaptureMetadataOutputObjectsDelegate> // 輸入輸出的中間橋樑 @property (nonatomic, strong) AVCaptureSession *session; // 掃描窗口 @property (nonatomic, strong) UIImageView *scanView; // 建立掃描視圖窗口,自定義方法 - (void)createdScanView { CGFloat margin = 50; CGRect scanFrame = CGRectMake(margin, margin + 20, self.view.bounds.size.width - margin * 2, self.view.bounds.size.width - margin * 2); self.scanView = [[UIImageView alloc] initWithFrame:scanFrame]; self.scanView.image = [UIImage imageNamed:@"scan_bg2"]; [self.view addSubview:self.scanView]; // 建立掃描 [self startScan]; } // 建立掃描,自定義方法 - (void)startScan { // 獲取攝像設備 AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; // 建立輸入流 AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil]; // 建立輸出流 AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init]; // 設置代理,在主線程裏刷新 [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; // 設置有效掃描區域 output.rectOfInterest = [self getScanCropWithScanViewFrame:self.scanView.frame readerViewBounds:self.view.bounds]; // 初始化連接對象 self.session = [[AVCaptureSession alloc] init]; // 設置採集率,高質量 [self.session setSessionPreset:AVCaptureSessionPresetHigh]; [self.session addInput:input]; [self.session addOutput:output]; // 設置掃碼支持的編碼格式(以下設置條形碼和二維碼兼容) output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code]; AVCaptureVideoPreviewLayer *layer = [AVCaptureVideoPreviewLayer layerWithSession:self.session]; layer.videoGravity = AVLayerVideoGravityResizeAspectFill; layer.frame = self.view.layer.bounds; [self.view.layer insertSublayer:layer atIndex:0]; // 開始捕獲 [self.session startRunning]; } // 設置掃描區域的比例關係,自定義方法 - (CGRect)getScanCropWithScanViewFrame:(CGRect)scanViewFrame readerViewBounds:(CGRect)readerViewBounds { CGFloat x, y, width, height; x = scanViewFrame.origin.y / readerViewBounds.size.height; y = scanViewFrame.origin.x / readerViewBounds.size.width; width = scanViewFrame.size.height / readerViewBounds.size.height; height = scanViewFrame.size.width / readerViewBounds.size.width; return CGRectMake(x, y, width, height); } // 獲取掃描結果,AVCaptureMetadataOutputObjectsDelegate 協議方法 - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection { if (metadataObjects.count > 0) { // 中止掃描 [self.session stopRunning]; AVMetadataMachineReadableCodeObject *metadataObject = [metadataObjects objectAtIndex:0]; // 獲取掃描結果 NSString *resultString = metadataObject.stringValue; // 輸出掃描字符串 [[[UIAlertView alloc] initWithTitle:@"掃描成功" message:resultString delegate:nil cancelButtonTitle:@"肯定" otherButtonTitles:nil] show]; } }
一些初始化的代碼加上實現代理方法便完成了二維碼掃描的工做,這裏咱們須要注意的是,在二維碼掃描的時候,咱們通常都會在屏幕中間放一個方框,用來顯示二維碼掃描的大小區間,這裏咱們在AVCaptureMetadataOutput 類中有一個 rectOfInterest 屬性,它的做用就是設置掃描範圍。這個 CGRect 參數和普通的 Rect 範圍不太同樣,它的四個值的範圍都是 0-1,表示比例。rectOfInterest 都是按照橫屏來計算的,因此當豎屏的狀況下 x 軸和 y 軸要交換一下,寬度和高度設置的狀況也是相似。微信
效果session
讀取主要用到 CoreImage 不過要強調的是讀取二維碼的功能只有在 iOS8 以後才支持,咱們須要在相冊中調用一個二維碼,將其讀取,代碼以下框架
// 遵照協議 <UIImagePickerControllerDelegate, UINavigationControllerDelegate> // 打開相冊,選取圖片,自定義方法 - (void)readQRCode { if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) { // 初始化相冊拾取器 UIImagePickerController *picker = [[UIImagePickerController alloc] init]; // 設置資源 picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; // 設置代理 picker.delegate = self; [self presentViewController:picker animated:YES completion:nil]; } else { NSString *errorStr = [NSString stringWithFormat:@"請在系統設置->隱私->照片中容許 \"%@\" 使用照片。", [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleNameKey]]; [[[UIAlertView alloc] initWithTitle:@"讀取失敗" message:errorStr delegate:nil cancelButtonTitle:@"肯定" otherButtonTitles:nil] show]; } } // 獲取選中的圖片,imagePickerController 協議方法 - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { // 獲取選擇的圖片 UIImage *image = info[UIImagePickerControllerOriginalImage]; // 識別圖片中的二維碼 NSString *resultString = [self recognizeQRCodeFromImage:image]; // 輸出掃描字符串 [[[UIAlertView alloc] initWithTitle:@"讀取成功" message:resultString delegate:nil cancelButtonTitle:@"肯定" otherButtonTitles:nil] show]; // 返回 [picker dismissViewControllerAnimated:YES completion:nil]; } // 識別圖片中的二維碼 - (NSString *)recognizeQRCodeFromImage:(UIImage *)image { CIImage *ciImage = [CIImage imageWithCGImage:image.CGImage]; // 初始化掃描儀,設置識別類型和識別質量 CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}]; // 掃描獲取的特徵組 NSArray *features = [detector featuresInImage:ciImage]; if (features.count >= 1) { // 獲取掃描結果 CIQRCodeFeature *feature = [features objectAtIndex:0]; NSString *resultString = feature.messageString; return resultString; } else { return @"該圖片不包含二維碼"; } }
效果ide
這個功能有不少的地方在用,最讓人熟知的我想即是微信了,其實實現方法仍是很簡單的。性能
// 建立圖片,添加長按手勢 - (void)recognizeQRCode { CGFloat margin = 50; CGRect scanFrame = CGRectMake(margin, margin + 20, self.view.bounds.size.width - margin * 2, self.view.bounds.size.width - margin * 2); UIImageView *imageView = [[UIImageView alloc] initWithFrame:scanFrame]; imageView.image = [UIImage imageNamed:@"demo"]; imageView.userInteractionEnabled = YES; [self.view addSubview:imageView]; UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(dealLongPress:)]; [imageView addGestureRecognizer:longPress]; } // 處理長按手勢,識別圖片中的二維碼 - (void)dealLongPress:(UIGestureRecognizer *)gesture { if (gesture.state == UIGestureRecognizerStateBegan) { UIImageView *pressedImageView = (UIImageView *)gesture.view; if (pressedImageView.image) { // 識別圖片中的二維碼 NSString *resultString = [self recognizeQRCodeFromImage:pressedImageView.image]; // 輸出掃描字符串 [[[UIAlertView alloc] initWithTitle:@"識別成功" message:resultString delegate:nil cancelButtonTitle:@"肯定" otherButtonTitles:nil] show]; } else { [[[UIAlertView alloc] initWithTitle:@"識別失敗" message:@"該圖片不包含二維碼" delegate:nil cancelButtonTitle:@"肯定" otherButtonTitles:nil] show]; } } } // 識別圖片中的二維碼 - (NSString *)recognizeQRCodeFromImage:(UIImage *)image { CIImage *ciImage = [CIImage imageWithCGImage:image.CGImage]; // 初始化掃描儀,設置識別類型和識別質量 CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}]; // 掃描獲取的特徵組 NSArray *features = [detector featuresInImage:ciImage]; if (features.count >= 1) { // 獲取掃描結果 CIQRCodeFeature *feature = [features objectAtIndex:0]; NSString *resultString = feature.messageString; return resultString; } else { return @"該圖片不包含二維碼"; } }
效果ui
生成二維碼,其實也是用到 CoreImage,可是步驟繁瑣一些,代碼以下
// 建立 ImageView,存放生成的二維碼 - (void)createQRCode { CGFloat margin = 50; CGRect frame = CGRectMake(margin, margin + 20, self.view.bounds.size.width - margin * 2, self.view.bounds.size.width - margin * 2); UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame]; [self.view addSubview:imageView]; // 生成二維碼 [self createQRCodeToImageView:imageView fromString:@"qianchia" withIcon:[UIImage imageNamed:@"demo1"] withColor:[UIColor redColor]]; } // 生成二維碼 - (void)createQRCodeToImageView:(UIImageView *)imageView fromString:(NSString *)inputString withIcon:(UIImage *)icon withColor:(UIColor *)color { // 建立過濾器 CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"]; // 恢復默認 [filter setDefaults]; // 給過濾器添加數據 NSData *data = [inputString dataUsingEncoding:NSUTF8StringEncoding]; // 經過 KVO 設置濾鏡 inputMessage 數據 [filter setValue:data forKey:@"inputMessage"]; // 設置二維碼顏色 UIColor *onColor = color ? : [UIColor blackColor]; UIColor *offColor = [UIColor whiteColor]; CIFilter *colorFilter = [CIFilter filterWithName:@"CIFalseColor" keysAndValues:@"inputImage", filter.outputImage, @"inputColor0", [CIColor colorWithCGColor:onColor.CGColor], @"inputColor1", [CIColor colorWithCGColor:offColor.CGColor], nil]; // 獲取輸出的二維碼 CIImage *outputImage = colorFilter.outputImage; // CIImage *outputImage = [filter outputImage]; CIContext *context = [CIContext contextWithOptions:nil]; CGImageRef cgImage = [context createCGImage:outputImage fromRect:[outputImage extent]]; // 將 CIImage 轉換成 UIImage,並放大顯示 UIImage *qrImage = [UIImage imageWithCGImage:cgImage scale:1.0 orientation:UIImageOrientationUp]; // 重繪 UIImage,默認狀況下生成的圖片比較模糊 CGFloat scale = 100; CGFloat width = qrImage.size.width * scale; CGFloat height = qrImage.size.height * scale; UIGraphicsBeginImageContext(CGSizeMake(width, height)); CGContextRef context1 = UIGraphicsGetCurrentContext(); CGContextSetInterpolationQuality(context1, kCGInterpolationNone); [qrImage drawInRect:CGRectMake(0, 0, width, height)]; qrImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); CGImageRelease(cgImage); // 添加頭像 if (icon) { UIGraphicsBeginImageContext(qrImage.size); [qrImage drawInRect:CGRectMake(0, 0, qrImage.size.width, qrImage.size.height)]; // 設置頭像大小 CGFloat scale = 5; CGFloat width = qrImage.size.width / scale; CGFloat height = qrImage.size.height / scale; CGFloat x = (qrImage.size.width - width) / 2; CGFloat y = (qrImage.size.height - height) / 2; [icon drawInRect:CGRectMake( x, y, width, height)]; UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); imageView.image = newImage; } else { imageView.image = qrImage; } }
效果
具體實現代碼見 GitHub 源碼 QExtension
// 包含頭文件 #import "QExtension.h"
掃描/識別二維碼
// 建立二維碼掃描視圖控制器 QQRCode *qrCode = [QQRCode q_qrCodeWithResult:^(BOOL isSucceed, NSString *result) { if (isSucceed) { [[[UIAlertView alloc] initWithTitle:@"Succeed" message:result delegate:nil cancelButtonTitle:@"肯定" otherButtonTitles:nil] show]; } else { [[[UIAlertView alloc] initWithTitle:@"Failed" message:result delegate:nil cancelButtonTitle:@"肯定" otherButtonTitles:nil] show]; } }]; // 設置個人二維碼信息 qrCode.myQRCodeInfo = @"http://weixin.qq.com/r/xUqbg1-ENgJJrRvg9x-X"; qrCode.headIcon = [UIImage imageNamed:@"demo6"]; // 打開掃描視圖控制器 [self presentViewController:qrCode animated:YES completion:nil];
效果
識別二維碼
// 建立圖片,添加長按手勢 self.imageView.image = [UIImage imageNamed:@"demo4"]; UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(dealLongPress:)]; [self.imageView addGestureRecognizer:longPress]; // 處理長按手勢,識別圖片中的二維碼 - (void)dealLongPress:(UIGestureRecognizer *)gesture { if (gesture.state == UIGestureRecognizerStateBegan) { UIImageView *pressedImageView = (UIImageView *)gesture.view; UIImage *image = pressedImageView.image; // 識別圖片中的二維碼 NSString *result = [image q_stringByRecognizeQRCode]; [[[UIAlertView alloc] initWithTitle:@"Succeed" message:result delegate:nil cancelButtonTitle:@"肯定" otherButtonTitles:nil] show]; } }
效果
生成二維碼
// 生成普通的二維碼 UIImage *qrImage = [UIImage q_imageWithQRCodeFromString:@"http://weixin.qq.com/r/xUqbg1-ENgJJrRvg9x-X" headIcon:nil color:nil backColor:nil]; // 生成帶頭像的二維碼 UIImage *qrImage = [UIImage q_imageWithQRCodeFromString:@"http://weixin.qq.com/r/xUqbg1-ENgJJrRvg9x-X" headIcon:[UIImage imageNamed:@"demo6"] color:[UIColor blackColor] backColor:[UIColor whiteColor]]; // 生成指定圖片大小的二維碼 UIImage *qrImage = [UIImage q_imageWithQRCodeFromString:@"http://weixin.qq.com/r/xUqbg1-ENgJJrRvg9x-X" imageSize:CGSizeMake(2048, 2048) headIcon:[UIImage imageNamed:@"demo6"] headFrame:CGRectMake(819, 819, 410, 410) color:nil backColor:nil];
效果
具體實現代碼見 GitHub 源碼 QExtension
// 包含頭文件 #import "QExtension.h"
掃描條形碼
生成條形碼
// 生成條形碼 UIImage *qrImage = [UIImage q_imageWithBarCodeFromString:@"cnblogs: QianChia" color:nil backColor:nil]; // 生成指定圖片大小的條形碼 UIImage *qrImage = [UIImage q_imageWithBarCodeFromString:@"cnblogs: QianChia" imageSize:CGSizeMake(1024, 512) color:[UIColor blueColor] backColor:[UIColor redColor]];
效果
使用 ZBarSDK,能夠掃描條形碼和二維碼。
配置
一、添加 SDK 的依賴庫和框架。在 項目設置 => TARGETS => Build Phases => Link Binary With Libraries 中依次添加如下庫或框架:
AVFoundation.framework CoreMedia.framework CoreVideo.framework QuartzCore.framework libiconv.tbd
二、因爲 ZBarSDK 不支持 Bitcode,須要在 Xcode 的項目設置 => TARGETS => Build Settings => Build Options => Enable Bitcode 的值設置爲 NO,不然沒法進行真機調試。
三、在 iOS8 + 系統中使用相機須要在 Info.plist 中添加 Privacy - Camera Usage Description,並設置其值。
四、在須要使用 ZBarSDK 的文件中
// 包含頭文件 #import "ZBarSDK.h" // 遵照協議 <ZBarReaderViewDelegate>
建立掃描
// 聲明掃描視圖 @property (nonatomic, strong) ZBarReaderView *scanView; // 實例化掃描視圖 self.scanView = [[ZBarReaderView alloc] init]; // 設置掃描視圖的位置尺寸 float width = self.view.bounds.size.width - 40; self.scanView.frame = CGRectMake(20, 30, width, width); // 設置代理人 self.scanView.readerDelegate = self; // 添加掃描視圖 [self.view addSubview:self.scanView]; // 關閉閃光燈 self.scanView.torchMode = 0; // 開始掃描 [self.scanView start];
獲取掃描結果
// ZBarReaderViewDelegate 協議方法 - (void)readerView:(ZBarReaderView *)readerView didReadSymbols:(ZBarSymbolSet *)symbols fromImage:(UIImage *)image { for (ZBarSymbol *symbol in symbols) { // 獲取掃描結果 NSString *scanString = symbol.data; // 顯示掃描結果 self.scanResult.text = scanString; } // 中止掃描 [self.scanView stop]; }
效果
使用 ZCZBarSDK,能夠掃描條形碼和二維碼。
配置
一、添加 SDK 的依賴庫和框架。在 項目設置 => TARGETS => Build Phases => Link Binary With Libraries 中依次添加如下庫或框架:
libiconv.tbd
二、因爲 ZCZBarSDK 不支持 Bitcode,須要在 Xcode 的項目設置 => TARGETS => Build Settings => Build Options => Enable Bitcode 的值設置爲 NO,不然沒法進行真機調試。
三、在 iOS8 + 系統中使用相機須要在 Info.plist 中添加 Privacy - Camera Usage Description,並設置其值。使用相冊須要在 Info.plist 中添加 Privacy - Photo Library Usage Description,並設置其值。
四、在須要使用 ZCZBarSDK 的文件中
// 包含頭文件 #import "ZCZBarViewController.h"
生成二維碼
// 將字符串 self.detailTF.text 中的內容生成的二維碼存放到 ImageView(self.codeImageView)中。 if (self.detailTF.text.length != 0) { [ZCZBarViewController createImageWithImageView:self.codeImageView String:self.detailTF.text Scale:100]; }
掃描二維碼
// isQRCode: 是否關閉條形碼掃描,專門掃描二維碼。 ZCZBarViewController *zzvc = [[ZCZBarViewController alloc] initWithIsQRCode:NO Block:^(NSString *result, BOOL isSucceed) { if (isSucceed) { self.scanResult.text = [NSString stringWithFormat:@"%@", result]; } }]; // 打開掃描視圖控制器 [self presentViewController:zzvc animated:YES completion:nil];
效果