前文:
?今天來詳細(xì)寫一下掃一掃實(shí)現(xiàn),基本上實(shí)現(xiàn)掃一掃有三個(gè)庫泳叠,分別是ZBar,ZXing,AVFoundation迂卢,在IOS7以前是沒有AVFoundation的。
?1.ZBar在掃描靈敏度和內(nèi)存上比ZXing較好途凫,缺點(diǎn)卻很多垢夹,比如有圓角的二維碼很難掃。目前已經(jīng)停止更新
?2.ZXing是GoogleCode上的一個(gè)開源的條形碼掃碼庫维费,是用java設(shè)計(jì)的果元,連Google Glass都在使用。但有人為了追求更高效率以及可移植性犀盟,出現(xiàn)了c++ port. Github上的Objectivc-C port而晒,其實(shí)就是用OC代碼封裝了一下而已,而且已經(jīng)停止維護(hù)且蓬。
?3.AVFoundation提供原生api掃描二維碼欣硼,無論在掃描靈敏度和性能上來說都是最優(yōu)的,所以毫無疑問我們應(yīng)該切換到AVFoundation,但是只兼容IOS7及以上诈胜,如果是IOS7之前的版本就要使用ZBar或ZXing代替豹障。
我寫的比較全面,二維碼焦匈,條形碼血公,掃描區(qū)域設(shè)置,光感傳感器應(yīng)用缓熟,閃光燈顯示與隱藏累魔,閃光燈打開與關(guān)閉,相冊獲取二維碼掃描
頭文件:?AVFoundation?是掃描需要用到的庫
?ImageIO?是閃光燈需要用到的庫
#import?<AVFoundation/AVFoundation.h>
#import?<ImageIO/ImageIO.h>
代理協(xié)議:
?AVCaptureMetadataOutputObjectsDelegate 掃描二維碼結(jié)果代理
?AVCaptureVideoDataOutputSampleBufferDelegate 周圍環(huán)境光感傳感器代理
?UINavigationControllerDelegate够滑、UIImagePickerControllerDelegate 選擇相冊圖片代理
<AVCaptureMetadataOutputObjectsDelegate,AVCaptureVideoDataOutputSampleBufferDelegate,UINavigationControllerDelegate,UIImagePickerControllerDelegate>
屬性:
//顯示圖層
@property(nonatomic,strong)AVCaptureVideoPreviewLayer *layer;
//捕捉會(huì)話
@property(nonatomic,strong)AVCaptureSession *session;
//輔助區(qū)域框
@property(nonatomic,strong)UIView * cyanView;
//打開燈光btn,默認(rèn)為hidden
@property(nonatomic,strong)UIButton * lightBtn;
//獲取相冊圖片進(jìn)行掃描
@property(nonatomic,strong)UIButton * photoBtn;
//建議先判斷一下垦写,是否開啟相機(jī)權(quán)限
//創(chuàng)建狀態(tài)對象
AVAuthorizationStatus authStatus =[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
//判斷攝像頭狀態(tài)是否可用
? ? if(authStatus==AVAuthorizationStatusAuthorized){
? ? ? ? //開始掃描二維碼
? ? ? ? [self startScan];
? ? }else{
? ? ? ? NSLog(@"未開啟相機(jī)權(quán)限,請前往設(shè)置中開啟");
? ? }
//開始掃描二維碼
-(void)startScan{
//1.創(chuàng)建捕捉會(huì)話,AVCaptureSession是第一個(gè)要被創(chuàng)建的對象彰触,所有的操作都要基于這一個(gè)session
? ? self.session = [[AVCaptureSession alloc]init];
? ? //2.添加輸入源(數(shù)據(jù)從攝像頭輸入)
? ? /*輸入源對應(yīng)的類是AVCaptureInput梯投,該類是一個(gè)抽象類,不能被直接實(shí)例化
? ? 在實(shí)際使用中况毅,都是使用他的子類分蓖,比如
? ? AVCaptureDeviceInput,
? ? AVCaptureScreenInput(只能用于Mac)尔许,
? ? AVCaptureMetadataInput么鹤,
? ? 一般情況下,我們是使用AVCaptureDeviceInput味廊,比如從設(shè)備的攝像頭或者麥克風(fēng)輸入蒸甜。
?? ? AVCaptureDeviceInput的實(shí)例化方法如下
? ? */
? ? AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
? ? AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
? ? [self.session addInput:input];
? ? //3.添加輸出數(shù)據(jù)(示例對象-->類對象-->元類對象-->根元類對象)
? ? /*輸入的類是AVCaptureInput,那么輸出的類相應(yīng)的就應(yīng)該是AVCaptureOutput毡们。
?? ? 輸出不需要和設(shè)備掛鉤,因?yàn)橐话闱闆r下迅皇,我們的輸出要么是音頻或視頻文件,要么是一些其他的數(shù)據(jù)衙熔,像二維碼掃描一般是字符串類型登颓。
?? ? 所以創(chuàng)建AVCaptureOutput實(shí)例就不需要AVCaptureDevice對象。
?? ? AVCaptureOutput也同樣是一個(gè)抽象類红氯,同樣要使用其子類框咙,在這里我們掃描二維碼,
?? ? 使用的是AVCaptureMetadataOutput痢甘,設(shè)置代碼如下所示*/
? ? AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
? ? [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
? ? //設(shè)置能掃描的區(qū)域喇嘱,這里注意,CGRectMake的x,y和width,height的值是互換位置
? ? output.rectOfInterest=CGRectMake(150/self.view.frame.size.height, 100/self.view.frame.size.width, (self.view.frame.size.width-200)/self.view.frame.size.width, (self.view.frame.size.width-200)/self.view.frame.size.width);
? ? [self.session addOutput:output];
? ? //設(shè)置輸入元數(shù)據(jù)的類型(類型是二維碼,條形碼數(shù)據(jù),注意塞栅,這個(gè)一定要寫在添加到session后面者铜,不然要崩潰,如果只需要掃描二維碼只需要AVMetadataObjectTypeQRCode,如果還需要掃描條形碼作烟,那么全部添加上)
? ? [output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode,
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? AVMetadataObjectTypeEAN8Code,
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? AVMetadataObjectTypeEAN13Code,
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? AVMetadataObjectTypeCode39Code,
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? AVMetadataObjectTypeCode39Mod43Code,
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? AVMetadataObjectTypeCode93Code,
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? AVMetadataObjectTypeCode128Code,
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? AVMetadataObjectTypePDF417Code,
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? AVMetadataObjectTypeAztecCode,
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? AVMetadataObjectTypeUPCECode,
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? AVMetadataObjectTypeInterleaved2of5Code,
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? AVMetadataObjectTypeITF14Code,
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? AVMetadataObjectTypeDataMatrixCode,
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ]];
? ? //4.添加掃描圖層
? ? self.layer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
? ? self.layer.videoGravity=AVLayerVideoGravityResizeAspectFill;
? ? self.layer.frame = self.view.bounds;
? ? [self.view.layer addSublayer:self.layer];
? ? //5.創(chuàng)建view,通過layer層進(jìn)行設(shè)置邊框?qū)挾群皖伾?用來輔助展示掃描的區(qū)域
? ? UIView * cyanView=[[UIView alloc] initWithFrame:CGRectMake(100, 150, self.view.frame.size.width-200, self.view.frame.size.width-200)];
? ? cyanView.layer.borderWidth=2;
? ? cyanView.layer.borderColor =[UIColor cyanColor].CGColor;
? ? [self.view addSubview:cyanView];
? ? //6.創(chuàng)建檢測光感源
? ? AVCaptureVideoDataOutput *guangOutPut = [[AVCaptureVideoDataOutput alloc] init];
? ? [guangOutPut setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
? ? //設(shè)置為高質(zhì)量采集率
? ? [self.session setSessionPreset:AVCaptureSessionPresetHigh];
? ? //把光感源添加到會(huì)話
? ? [self.session addOutput:guangOutPut];
? ? //7.閃光燈開關(guān)btn
? ? self.lightBtn=[[UIButton alloc]initWithFrame:CGRectMake(150, cyanView.frame.origin.y+cyanView.frame.size.height+100, self.view.frame.size.width-300, self.view.frame.size.width-300)];
? ? self.lightBtn.backgroundColor=[UIColor grayColor];
? ? [self.lightBtn addTarget:self action:@selector(lightBtnClick:) forControlEvents:UIControlEventTouchUpInside];
? ? [self.view addSubview:self.lightBtn];
? ? //8.獲取相冊圖片進(jìn)行掃描btn
? ? self.photoBtn=[[UIButton alloc]initWithFrame:CGRectMake(100, self.lightBtn.frame.origin.y+self.lightBtn.frame.size.height+50, self.view.frame.size.width-200, 50)];
? ? [self.photoBtn setTitle:@"相冊" forState:UIControlStateNormal];
? ? [self.photoBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
? ? self.photoBtn.backgroundColor=[UIColor orangeColor];
? ? [self.photoBtn addTarget:self action:@selector(photoBtnClick:) forControlEvents:UIControlEventTouchUpInside];
? ? [self.view addSubview:self.photoBtn];
? ? //9.開始掃描
? ? [self.session startRunning];
}
//實(shí)現(xiàn)掃描的回調(diào)代理方法
- (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputMetadataObjects:(NSArray*)metadataObjects fromConnection:(AVCaptureConnection*)connection{
//如果數(shù)組metadataObjects中有數(shù)據(jù)愉粤,metadataObjects是個(gè)數(shù)組類型
? ? if(metadataObjects.count>0) {
? ? ? ? // 獲取最終的讀取結(jié)果,獲取數(shù)組中最后一個(gè)元素拿撩,數(shù)組中是AVMetadataMachineReadableCodeObject類型對象
? ? ? ? AVMetadataMachineReadableCodeObject*object = [metadataObjects lastObject];
? ? ? ? NSLog(@"%@",object.stringValue);
? ? ? ? //停止掃描
? ? ? ? [self.session stopRunning];
? ? ? ? //移除掃描層layer
? ? ? ? [self.layer removeFromSuperlayer];
? ? }else{
? ? ? ? NSLog(@"沒有掃描到數(shù)據(jù)");
? ? }
}
//光感傳感器代理
-(void)captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection*)connection{
? ? //獲取光線的值
? ? CFDictionaryRef metadataDict = CMCopyDictionaryOfAttachments(NULL,sampleBuffer, kCMAttachmentMode_ShouldPropagate);
? ? NSDictionary *metadata = [[NSMutableDictionary alloc] initWithDictionary:(__bridge NSDictionary*)metadataDict];
? ? CFRelease(metadataDict);
? ? NSDictionary *exifMetadata = [[metadata objectForKey:(NSString *)kCGImagePropertyExifDictionary] mutableCopy];
? ? floatbrightnessValue = [[exifMetadata objectForKey:(NSString*)kCGImagePropertyExifBrightnessValue]floatValue];
? ? NSLog(@"%f",brightnessValue);
? ? // 根據(jù)brightnessValue的值來打開和關(guān)閉閃光燈衣厘,一般值小于0就需要打開,大于0就關(guān)閉
? ? if((brightnessValue <0)) {//顯示閃光燈
? ? ? ? self.lightBtn.hidden=NO;
? ? }elseif((brightnessValue >0)) {//隱藏閃光燈
? ? ? ? self.lightBtn.hidden=YES;
? ? }
}
//閃光燈按鈕點(diǎn)擊方法
-(void)lightBtnClick:(UIButton*)sender{
? ? //判斷當(dāng)前設(shè)備是否有閃光燈
? ? AVCaptureDevice * device=[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
? ? BOOL result=[device hasTorch];
? ? if(result==YES){
? ? ? ? if(self.lightBtn.isSelected==NO){
? ? ? ? ? ? self.lightBtn.selected=YES;
? ? ? ? ? ? self.lightBtn.backgroundColor=[UIColor greenColor];
? ? ? ? ? ? [device lockForConfiguration:nil];
? ? ? ? ? ? [device setTorchMode: AVCaptureTorchModeOn];//開
? ? ? ? ? ? [device unlockForConfiguration];
? ? ? ? }else if(self.lightBtn.isSelected==YES){
? ? ? ? ? ? self.lightBtn.selected=NO;
? ? ? ? ? ? self.lightBtn.backgroundColor=[UIColor grayColor];;
? ? ? ? ? ? [device lockForConfiguration:nil];
? ? ? ? ? ? [device setTorchMode: AVCaptureTorchModeOff];//關(guān)
? ? ? ? ? ? [device unlockForConfiguration];
? ? ? ? }
? ? }else{
? ? ? ? NSLog(@"當(dāng)前設(shè)備閃光燈不可用");
? ? }
}
//獲取相冊圖片btn點(diǎn)擊方法
-(void)photoBtnClick:(UIButton*)sender{
? ? UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
? ? imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
? ? // imagePicker.allowsEditing = YES;
? ? imagePicker.delegate=self;
? ? [self.navigationController presentViewController:imagePicker animated:YES completion:nil];
}
//UIImagePickerControllerDelegate選擇圖片的回調(diào)
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *)info {
?//把UIimage類型轉(zhuǎn)換成CIimage類型
? ? UIImage *pickedImage = info[UIImagePickerControllerEditedImage] ?: info[UIImagePickerControllerOriginalImage];
? ? CIImage*detectImage = [CIImage imageWithData:UIImagePNGRepresentation(pickedImage)];
? ? //解析掃描二維碼結(jié)果字符串
? ? CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy: CIDetectorAccuracyLow}];
? ? CIQRCodeFeature*feature = (CIQRCodeFeature*)[detector featuresInImage:detectImage options:nil].firstObject;
? ? [picker dismissViewControllerAnimated:YES completion:^{
? ? ? ? if(feature.messageString) {
? ? ? ? ? ? NSLog(@"=================二維碼結(jié)果為%@",feature.messageString);
? ? ? ? }else{
? ? ? ? ? ? NSLog(@"未掃描到相應(yīng)二維碼");
? ? ? ? }
? ? ? ? //停止會(huì)話對象掃描
? ? ? ? [self.session stopRunning];
? ? ? ? //移除掃描層layer
? ? ? ? [self.layer removeFromSuperlayer];
? ? }];
}