原文:http://strivingboy.github.io/blog/2014/11/08/scan-qrcode/
關於二維碼掃描有不少優秀第三方庫如:
最近項目需要,看了下使用ios7自帶的 AVFoundation Framework 來實現二維碼掃描,Demo 見:scan_qrcode_demo
關於AVFoundation
AVFoundation 是一個很大基礎庫,用來創建基於時間的視聽媒體,可以使用它來檢查,創建、編輯或媒體文件。也可以輸入流從設備和操作視頻實時捕捉和回放。詳細框架介紹見官網:About AV Foundation,本文只是介紹如果使用AVFoundation獲取二維碼。
首先獲取流媒體信息我們需要AVCaptureSession
對象來管理輸入流和輸出流,AVCaptureVideoPreviewLayer
對象來顯示信息,基本流程如下圖所示:

注:
AVCaptureSession
管理輸入(AVCaptureInput)和輸出(AVCaptureOutput)流,包含開啟和停止會話方法。
AVCaptureDeviceInput
是AVCaptureInput的子類,可以作為輸入捕獲會話,用AVCaptureDevice實例初始化。
AVCaptureDevice
代表了物理捕獲設備如:攝像機。用於配置等底層硬件設置相機的自動對焦模式。
AVCaptureMetadataOutput
是AVCaptureOutput的子類,處理輸出捕獲會話。捕獲的對象傳遞給一個委托實現AVCaptureMetadataOutputObjectsDelegate協議。協議方法在指定的派發隊列(dispatch queue)上執行。
AVCaptureVideoPreviewLayer
CALayer的一個子類,顯示捕獲到的相機輸出流。
下面看下實現過程如下:
Step1:需要導入:AVFoundation Framework 包含頭文件:
#import <AVFoundation/AVFoundation.h>
Step2:設置捕獲會話
設置 AVCaptureSession 和 AVCaptureVideoPreviewLayer 成員
1
2 3 4 5 6 7 8 9 10 |
#import <AVFoundation/AVFoundation.h> static const char *kScanQRCodeQueueName = "ScanQRCodeQueue"; @interface ViewController () <AVCaptureMetadataOutputObjectsDelegate> ..... @property (nonatomic) AVCaptureSession *captureSession; @property (nonatomic) AVCaptureVideoPreviewLayer *videoPreviewLayer; @property (nonatomic) BOOL lastResult; @end |
Step3:創建會話,讀取輸入流
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
- (BOOL)startReading { // 獲取 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:_sanFrameView.layer.bounds]; [_sanFrameView.layer addSublayer:_videoPreviewLayer]; // 開始會話 [_captureSession startRunning]; return YES; } |
Step4:停止讀取
1
2 3 4 5 6 7 |
- (void)stopReading { // 停止會話 [_captureSession stopRunning]; _captureSession = nil; } |
Step5:獲取捕獲數據
1
2 3 4 5 6 7 8 9 10 11 12 13 14 |
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection { if (metadataObjects != nil && [metadataObjects count] > 0) { AVMetadataMachineReadableCodeObject *metadataObj = [metadataObjects objectAtIndex:0]; NSString *result; if ([[metadataObj type] isEqualToString:AVMetadataObjectTypeQRCode]) { result = metadataObj.stringValue; } else { NSLog(@"不是二維碼"); } [self performSelectorOnMainThread:@selector(reportScanResult:) withObject:result waitUntilDone:NO]; } } |
Step6:處理結果
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
- (void)reportScanResult:(NSString *)result { [self stopReading]; if (!_lastResult) { return; } _lastResut = NO; UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"二維碼掃描" message:result delegate:nil cancelButtonTitle:@"取消" otherButtonTitles: nil]; [alert show]; // 以下處理了結果,繼續下次掃描 _lastResult = YES; } |
以上基本就是二維碼的獲取流程,和掃一掃二維碼伴隨的就是開啟系統照明,這個比較簡單,也是利用 AVCaptureDevice
,請看如下實現:
1
2 3 4 5 6 7 8 9 10 11 12 13 |
- (void)systemLightSwitch:(BOOL)open { AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; if ([device hasTorch]) { [device lockForConfiguration:nil]; if (open) { [device setTorchMode:AVCaptureTorchModeOn]; } else { [device setTorchMode:AVCaptureTorchModeOff]; } [device unlockForConfiguration]; } } |
以上就是本文介紹的大部分內容,詳細代碼請看demo scan_qrcode_deomo
實現過程中遇到一下兩個問題:
1、掃描一個二維碼,輸出流回重復調用,代理方法頭文件介紹:
1
2 3 4 5 6 7 8 9 10 11 12 13 |
/*! @method captureOutput:didOutputMetadataObjects:fromConnection: ..... @discussion Delegates receive this message whenever the output captures and emits new objects, as specified by its metadataObjectTypes property. Delegates can use the provided objects in conjunction with other APIs for further processing. This method will be called on the dispatch queue specified by the output‘s metadataObjectsCallbackQueue property. **This method may be called frequently** so it must be efficient to prevent capture performance problems, including dropped metadata objects. Clients that need to reference metadata objects outside of the scope of this method must retain them and then release them when they are finished with them. */ |
代理方法會頻繁調用,我暫且用一個標記(@property (nonatomic) BOOL lastResult)表示是否是第一次掃描成功,來處理。
2、AVFoundation 該庫不能掃描相冊中的二維碼圖片,不知為啥蘋果沒有支持,有知道實現的麻煩告訴我哈。
參考鏈接