1.基本概念
要了解如何通過AVFoundation掃描二維碼,首先要了解下面幾個(gè)類
<code>AVCaptureSession</code>
關(guān)于AVCaptureSession,我在前面一個(gè)文章里已經(jīng)有所描述,可以去前面文章看下,其實(shí)它是一個(gè)連通輸入和輸出設(shè)備的橋梁AVCaptureSession概述
<code>AVCaptureDevice</code>
AVCaptureDevice,獲取手機(jī)的相機(jī)設(shè)備,通常使用下面方法進(jìn)行初始化
<code>+ (AVCaptureDevice *)defaultDeviceWithMediaType:(NSString *)mediaType;</code>
可以調(diào)節(jié)以下的功能:1. 前置和后置攝像頭2. 閃光燈開關(guān)3. 手電筒開關(guān)4. 焦距調(diào)整5. 曝光量調(diào)節(jié)6. 白平衡
<code>AVCaptureVideoPreviewLayer</code>
視頻抓取的預(yù)覽層,基于CALayer,顯示捕獲到的相機(jī)輸出流晒来。
<code>AVCaptureDeviceInput</code>
是AVCaptureInput的子類,可以作為輸入捕獲會(huì)話朵诫,用AVCaptureDevice實(shí)例初始化掖看。
<code>AVCaptureMetadataOutput</code>
是AVCaptureOutput的子類,處理輸出捕獲會(huì)話邑飒。捕獲的對(duì)象傳遞給一個(gè)委托實(shí)現(xiàn)AVCaptureMetadataOutputObjectsDelegate協(xié)議循签。協(xié)議方法在指定的派發(fā)隊(duì)列(dispatch queue)上執(zhí)行级乐。
2 初始化
定義成員變量
@property (strong, nonatomic)AVCaptureVideoPreviewLayer *preview;
@property (nonatomic, strong) AVCaptureDeviceInput *activeVideoInput;
@property (nonatomic, strong) AVCaptureMetadataOutput *metadataOutPut;
@property (nonatomic, strong) AVCaptureSession *captureSeesion;
初始化方法
- (BOOL)setUpSession{
NSError *error;
// 初始化會(huì)話
self.captureSeesion = [[AVCaptureSession alloc] init];
self.captureSeesion.sessionPreset = AVCaptureSessionPresetHigh;
// 初始化默認(rèn)輸入設(shè)備
AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// 默認(rèn)的視頻捕捉設(shè)備
self.activeVideoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error];
if (error) {
// 處理錯(cuò)誤信息
return NO;
}
// 初始化輸出設(shè)備
if (self.activeVideoInput) {
if ([self.captureSeesion canAddInput: self.activeVideoInput]) {
[self.captureSeesion addInput: self.activeVideoInput];
}
}else{
// 處理錯(cuò)誤信息
return NO;
}
// 初始化輸出設(shè)備
self.metadataOutPut = [[AVCaptureMetadataOutput alloc] init];
if ([self.captureSeesion canAddOutput:self.metadataOutPut]) {
[self.captureSeesion addOutput:self.metadataOutPut];
dispatch_queue_t mainQueue = dispatch_get_main_queue();
[self.metadataOutPut setMetadataObjectsDelegate:self queue:mainQueue];
// 支持的掃描樣式
NSArray *types = @[AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code];
self.metadataOutPut.metadataObjectTypes = types;
}else{
// 處理錯(cuò)誤信息
return NO;
}
return YES;
}
添加AVCaptureVideoPreviewLayer到視圖中
BOOL isSuccess= [self setUpSession];
if (isSuccess) {
self.preview = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSeesion];
self.preview.videoGravity = AVLayerVideoGravityResizeAspectFill;
//把拍攝的layer添加到主視圖的layer中
[self.view.layer insertSublayer:self.preview atIndex:0];
self.preview.frame = self.view.bounds;
[self.captureSeesion startRunning];
}
實(shí)現(xiàn)代理方法
//掃描完成的時(shí)候就會(huì)調(diào)用
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
//終止會(huì)話
[self.captureSeesion stopRunning];
//把掃描的layer從主視圖的layer中移除
[self.preview removeFromSuperlayer];
NSString *val = nil;
if (metadataObjects.count > 0)
{
//取出最后掃描到的對(duì)象
AVMetadataMachineReadableCodeObject *obj = metadataObjects[0];
//獲得掃描的結(jié)果
val = obj.stringValue;
NSLog(@"%@",val);
// 可以執(zhí)行回調(diào)函數(shù)
}
}
3.other
類似微信二維碼掃描
可以在AVCaptureVideoPreviewLayer上添加一個(gè)View,然后講掃描范圍縮小到一個(gè)指定的區(qū)域,可以提升掃描速度和性能的.
通過設(shè)置<code>AVCaptureMetadataOutput</code>的<code>rectOfInterest</code>屬性來約束掃描范圍
/*!
@property rectOfInterest
@abstract
Specifies a rectangle of interest for limiting the search area for visual metadata.
@discussion
The value of this property is a CGRect that determines the receiver's rectangle of interest for each frame of video.
The rectangle's origin is top left and is relative to the coordinate space of the device providing the metadata. Specifying
a rectOfInterest may improve detection performance for certain types of metadata. The default value of this property is the
value CGRectMake(0, 0, 1, 1). Metadata objects whose bounds do not intersect with the rectOfInterest will not be returned.
*/
@property(nonatomic) CGRect rectOfInterest NS_AVAILABLE_IOS(7_0);
<code>rectOfInterest</code>是一個(gè)基于X,Y,Width和Height的比例,是基于圖像的大小裁剪的疙咸。
可以通過對(duì)下面的代碼進(jìn)行修改,裁剪
CGSize size = self.view.bounds.size;
CGRect cropRect = CGRectMake(40, 100, 240, 240);
CGFloat p1 = size.height/size.width;
CGFloat p2 = 1920./1080.; //使用了1080p的圖像輸出
if (p1 < p2) {
CGFloat fixHeight = bounds.size.width * 1920. / 1080.;
CGFloat fixPadding = (fixHeight - size.height)/2;
captureOutput.rectOfInterest = CGRectMake((cropRect.origin.y + fixPadding)/fixHeight,
cropRect.origin.x/size.width,
cropRect.size.height/fixHeight,
cropRect.size.width/size.width);
} else {
CGFloat fixWidth = bounds.size.height * 1080. / 1920.;
CGFloat fixPadding = (fixWidth - size.width)/2;
captureOutput.rectOfInterest = CGRectMake(cropRect.origin.y/size.height,
(cropRect.origin.x + fixPadding)/fixWidth,
cropRect.size.height/size.height,
cropRect.size.width/fixWidth);
}
4.補(bǔ)充
通常情況下,我們還要對(duì)手機(jī)是否支持?jǐn)z像頭進(jìn)行判斷,或者是否開啟對(duì)攝像頭的權(quán)限.
判斷攝像頭是否可用(在模擬器上返回no)
-(BOOL)isCameraValid {
return [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] &&
[UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear];
}
判斷攝像頭的權(quán)限是否開啟
- (BOOL)isCameraAllowed {
NSString *mediaType = AVMediaTypeVideo;
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:mediaType];
if(authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied){
return NO;
}
return YES;
}
可以通過進(jìn)行這兩個(gè)判斷,然后選擇是否彈出掃描的控制器
可以通過下載github上的例子查看
https://github.com/Raul7777/XHScanTool