敲代碼前的工作:
- 原生掃碼支持iOS7.0及更高版本,如果你的項(xiàng)目要支持iOS6,可以用ZBar。
- 在Info.plist里面加上使用相機(jī)的描述
Privacy - Camera Usage Description,如圖最后一行
CameraDescription.png
好,進(jìn)入正題。首先導(dǎo)入頭文件:
#import <AVFoundation/AVFoundation.h>
定義屬性AVCaptureSession
@interface ViewController ()<AVCaptureMetadataOutputObjectsDelegate>
@property (nonatomic, strong) AVCaptureSession *session;
@end
請(qǐng)求相機(jī)權(quán)限
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if (granted) {
dispatch_async(dispatch_get_main_queue(), ^{
[self loadScanView];
});
} else {
NSLog(@"無(wú)權(quán)限訪問(wèn)相機(jī)");
}
}];
}
注意:
(1)請(qǐng)求權(quán)限的參數(shù)mediaType只能是AVMediaTypeVideo或者AVMediaTypeAudio,傳其它類型會(huì)拋出異常,前者就是相機(jī)權(quán)限,后者是音頻,也就是麥克風(fēng)權(quán)限。
(2)completionHandler是分線程的回調(diào),授權(quán)以后創(chuàng)建界面的操作要回到主線程,否則點(diǎn)完確定要等一陣子界面才加載出來(lái)。官方文檔說(shuō)"The completion handler is called on an arbitrary dispatch queue.",就是說(shuō)在隨機(jī)的線程里調(diào)用,我們就當(dāng)是分線程吧。其它請(qǐng)求相冊(cè)、麥克風(fēng)、通訊錄等權(quán)限的回調(diào)里面,都要回到主線程操作。
創(chuàng)建掃描界面
- (void)loadScanView {
//獲取攝像設(shè)備
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
//創(chuàng)建輸入流
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
//創(chuàng)建輸出流
AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc]init];
//設(shè)置代理 在主線程里刷新
[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
//初始化鏈接對(duì)象
self.session = [[AVCaptureSession alloc]init];
//高質(zhì)量采集率
[self.session setSessionPreset:AVCaptureSessionPresetHigh];
[self.session addInput:input];
[self.session addOutput:output];
//設(shè)置掃碼支持的編碼格式(如下設(shè)置條形碼和二維碼兼容)
output.metadataObjectTypes=@[AVMetadataObjectTypeQRCode,//二維碼
//以下為條形碼,如果項(xiàng)目只需要掃描二維碼,下面都不要寫(xiě)
AVMetadataObjectTypeEAN13Code,
AVMetadataObjectTypeEAN8Code,
AVMetadataObjectTypeUPCECode,
AVMetadataObjectTypeCode39Code,
AVMetadataObjectTypeCode39Mod43Code,
AVMetadataObjectTypeCode93Code,
AVMetadataObjectTypeCode128Code,
AVMetadataObjectTypePDF417Code];
AVCaptureVideoPreviewLayer *layer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
layer.frame = self.view.layer.bounds;
[self.view.layer insertSublayer:layer atIndex:0];
//開(kāi)始捕獲
[self.session startRunning];
}
如果你的項(xiàng)目只需要掃描二維碼,不考慮條形碼,那么恭喜你,metadataObjectTypes只需要AVMetadataObjectTypeQRCode就夠了,識(shí)別速度杠杠的。如果要支持條形碼掃描,那下面的幾個(gè)類型都加上。but,加的類型越多,掃描效率越低。因?yàn)槲覀冺?xiàng)目要掃描商品條形碼,為了掃描效率更高,開(kāi)始就加了幾種常見(jiàn)的條形碼編碼類型,后來(lái)測(cè)試發(fā)現(xiàn)有些條形碼識(shí)別不了,無(wú)奈度娘查詢一番,又加了幾種類型,最終確定這8個(gè),基本涵蓋所有條形碼編碼格式,半年了也沒(méi)客戶反饋哪個(gè)碼識(shí)別不了,識(shí)別效率也不錯(cuò),沒(méi)有明顯延遲。
下一步,獲取掃描結(jié)果
#pragma mark - AVCaptureMetadataOutputObjectsDelegate
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
if (metadataObjects.count>0) {
[self.session stopRunning];
AVMetadataMachineReadableCodeObject *metadataObject = metadataObjects[0];
NSLog(@"%@",metadataObject.stringValue);
}
}
討論:
AVCaptureMetadataOutput的rectOfInterest屬性會(huì)嚴(yán)重影響掃描效率,如果掃描中心點(diǎn)在屏幕中央,像這樣:

那么再次恭喜你,這個(gè)屬性不用設(shè)置,默認(rèn)就好。如掃描區(qū)域的中心點(diǎn)不在屏幕中央,比如微信那樣偏上面一丟丟,那你就要考慮
rectOfInterest這個(gè)神奇的屬性了。因?yàn)槲覀冺?xiàng)目的設(shè)計(jì)就是上圖那樣,所以我也沒(méi)去細(xì)致研究rectOfInterest怎么設(shè)置,不過(guò)這里有篇文章講得很透徹。
