IOS 原生二維碼掃描

前文:

?今天來詳細寫一下掃一掃實現(xiàn),基本上實現(xiàn)掃一掃有三個庫,分別是ZBar,ZXing,AVFoundation,在IOS7以前是沒有AVFoundation的。

?1.ZBar在掃描靈敏度和內存上比ZXing較好,缺點卻很多,比如有圓角的二維碼很難掃。目前已經停止更新

?2.ZXing是GoogleCode上的一個開源的條形碼掃碼庫,是用java設計的,連Google Glass都在使用。但有人為了追求更高效率以及可移植性,出現(xiàn)了c++ port. Github上的Objectivc-C port,其實就是用OC代碼封裝了一下而已,而且已經停止維護。

?3.AVFoundation提供原生api掃描二維碼,無論在掃描靈敏度和性能上來說都是最優(yōu)的,所以毫無疑問我們應該切換到AVFoundation,但是只兼容IOS7及以上,如果是IOS7之前的版本就要使用ZBar或ZXing代替。

我寫的比較全面,二維碼,條形碼,掃描區(qū)域設置,光感傳感器應用,閃光燈顯示與隱藏,閃光燈打開與關閉,相冊獲取二維碼掃描




頭文件:

?AVFoundation?是掃描需要用到的庫

?ImageIO?是閃光燈需要用到的庫

#import?<AVFoundation/AVFoundation.h>

#import?<ImageIO/ImageIO.h>



代理協(xié)議:

?AVCaptureMetadataOutputObjectsDelegate 掃描二維碼結果代理

?AVCaptureVideoDataOutputSampleBufferDelegate 周圍環(huán)境光感傳感器代理

?UINavigationControllerDelegate、UIImagePickerControllerDelegate 選擇相冊圖片代理

<AVCaptureMetadataOutputObjectsDelegate,AVCaptureVideoDataOutputSampleBufferDelegate,UINavigationControllerDelegate,UIImagePickerControllerDelegate>



屬性:

//顯示圖層

@property(nonatomic,strong)AVCaptureVideoPreviewLayer *layer;

//捕捉會話

@property(nonatomic,strong)AVCaptureSession *session;

//輔助區(qū)域框

@property(nonatomic,strong)UIView * cyanView;

//打開燈光btn,默認為hidden

@property(nonatomic,strong)UIButton * lightBtn;

//獲取相冊圖片進行掃描

@property(nonatomic,strong)UIButton * photoBtn;



//建議先判斷一下,是否開啟相機權限

//創(chuàng)建狀態(tài)對象

AVAuthorizationStatus authStatus =[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];

//判斷攝像頭狀態(tài)是否可用

? ? if(authStatus==AVAuthorizationStatusAuthorized){

? ? ? ? //開始掃描二維碼

? ? ? ? [self startScan];

? ? }else{

? ? ? ? NSLog(@"未開啟相機權限,請前往設置中開啟");

? ? }



//開始掃描二維碼

-(void)startScan{


//1.創(chuàng)建捕捉會話,AVCaptureSession是第一個要被創(chuàng)建的對象,所有的操作都要基于這一個session

? ? self.session = [[AVCaptureSession alloc]init];


? ? //2.添加輸入源(數(shù)據(jù)從攝像頭輸入)

? ? /*輸入源對應的類是AVCaptureInput,該類是一個抽象類,不能被直接實例化

? ? 在實際使用中,都是使用他的子類,比如

? ? AVCaptureDeviceInput,

? ? AVCaptureScreenInput(只能用于Mac),

? ? AVCaptureMetadataInput,

? ? 一般情況下,我們是使用AVCaptureDeviceInput,比如從設備的攝像頭或者麥克風輸入。

?? ? AVCaptureDeviceInput的實例化方法如下

? ? */

? ? AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

? ? AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];

? ? [self.session addInput:input];


? ? //3.添加輸出數(shù)據(jù)(示例對象-->類對象-->元類對象-->根元類對象)

? ? /*輸入的類是AVCaptureInput,那么輸出的類相應的就應該是AVCaptureOutput。

?? ? 輸出不需要和設備掛鉤,因為一般情況下,我們的輸出要么是音頻或視頻文件,要么是一些其他的數(shù)據(jù),像二維碼掃描一般是字符串類型。

?? ? 所以創(chuàng)建AVCaptureOutput實例就不需要AVCaptureDevice對象。

?? ? AVCaptureOutput也同樣是一個抽象類,同樣要使用其子類,在這里我們掃描二維碼,

?? ? 使用的是AVCaptureMetadataOutput,設置代碼如下所示*/

? ? AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];

? ? [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];

? ? //設置能掃描的區(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ù)據(jù)的類型(類型是二維碼,條形碼數(shù)據(jù),注意,這個一定要寫在添加到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層進行設置邊框寬度和顏色,用來輔助展示掃描的區(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()];

? ? //設置為高質量采集率

? ? [self.session setSessionPreset:AVCaptureSessionPresetHigh];

? ? //把光感源添加到會話

? ? [self.session addOutput:guangOutPut];


? ? //7.閃光燈開關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.獲取相冊圖片進行掃描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];

}



//實現(xiàn)掃描的回調代理方法

- (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputMetadataObjects:(NSArray*)metadataObjects fromConnection:(AVCaptureConnection*)connection{

//如果數(shù)組metadataObjects中有數(shù)據(jù),metadataObjects是個數(shù)組類型

? ? if(metadataObjects.count>0) {

? ? ? ? // 獲取最終的讀取結果,獲取數(shù)組中最后一個元素,數(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的值來打開和關閉閃光燈,一般值小于0就需要打開,大于0就關閉

? ? if((brightnessValue <0)) {//顯示閃光燈

? ? ? ? self.lightBtn.hidden=NO;

? ? }elseif((brightnessValue >0)) {//隱藏閃光燈

? ? ? ? self.lightBtn.hidden=YES;

? ? }

}



//閃光燈按鈕點擊方法

-(void)lightBtnClick:(UIButton*)sender{

? ? //判斷當前設備是否有閃光燈

? ? 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];//關

? ? ? ? ? ? [device unlockForConfiguration];

? ? ? ? }

? ? }else{

? ? ? ? NSLog(@"當前設備閃光燈不可用");

? ? }

}



//獲取相冊圖片btn點擊方法

-(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選擇圖片的回調

-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *)info {

?//把UIimage類型轉換成CIimage類型

? ? UIImage *pickedImage = info[UIImagePickerControllerEditedImage] ?: info[UIImagePickerControllerOriginalImage];

? ? CIImage*detectImage = [CIImage imageWithData:UIImagePNGRepresentation(pickedImage)];


? ? //解析掃描二維碼結果字符串

? ? 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(@"=================二維碼結果為%@",feature.messageString);

? ? ? ? }else{

? ? ? ? ? ? NSLog(@"未掃描到相應二維碼");

? ? ? ? }

? ? ? ? //停止會話對象掃描

? ? ? ? [self.session stopRunning];

? ? ? ? //移除掃描層layer

? ? ? ? [self.layer removeFromSuperlayer];

? ? }];

}

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容