ios7 蘋果原生二維碼掃描(和微信類似)


在ios7蘋果推出了二維碼掃描,以前想要做二維碼掃描,只能通過第三方ZBar與ZXing。

ZBar在掃描的靈敏度上,和內存的使用上相對於ZXing上都是較優的,但是對於 “圓角二維碼” 的掃描確很困難。

ZXing 是 Google Code上的一個開源的條形碼掃描庫,是用java設計的,連Google Glass 都在使用的。但有人為了追求更高效率以及可移植性,出現了c++ port. Github上的Objectivc-C port,其實就是用OC代碼封裝了一下而已,而且已經停止維護。這樣效率非常低,在instrument下面可以看到CPU和內存瘋漲,在內存小的機器上很容易崩潰。

AVFoundation無論在掃描靈敏度和性能上來說都是最優的。

首先要導入#import <AVFoundation/AVFoundation.h>框架

 

 其次還需要授權應用可以訪問相機

    // 判斷相機是否授權使用相機
    AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    if(status == AVAuthorizationStatusAuthorized) {
        
    } else if(status == AVAuthorizationStatusDenied){
       // NSLog(@"denied不允許");
        return ;
    } else if(status == AVAuthorizationStatusNotDetermined){
        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
            if(granted){
//                NSLog(@"允許");
            } else {
//                NSLog(@"不允許");
                return;
            }
        }];
    }
    
    //    typedef enum
//        AVAuthorizationStatusNotDetermined = 0, // 用戶尚未做出選擇這個應用程序的問候
//        AVAuthorizationStatusRestricted,        // 此應用程序沒有被授權訪問的照片數據。可能是家長控制權限
//        AVAuthorizationStatusDenied,            // 用戶已經明確否認了這一照片數據的應用程序訪問
//        AVAuthorizationStatusAuthorized         // 用戶已經授權應用訪問照片數據} CLAuthorizationStatus;

 

 

完成二維碼掃描大致有十個步驟:

    // 1.獲取輸入設備
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    
    // 2.創建輸入對象
    NSError *error;
    AVCaptureDeviceInput *inPut = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
    
    if (inPut == nil) {
        UIAlertView *aler = [[UIAlertView alloc] initWithTitle:@"提示" message:@"設備不可用" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"確定", nil];
        [self.view addSubview:aler];
        [aler show];
        return;
    }
    
    // 3.創建輸出對象
    AVCaptureMetadataOutput *outPut = [[AVCaptureMetadataOutput alloc] init];
    
    // 4.設置代理監聽輸出對象的輸出流  (說明:使用主線程隊列,相應比較同步,使用其他隊列,相應不同步,容易讓用戶產生不好的體驗)
    [outPut setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    
    // 5.創建會話
    AVCaptureSession *session = [[AVCaptureSession alloc] init];
    self.session = session;
    
    // 6.將輸入和輸出對象添加到會話
    if ([session canAddInput:inPut]) {
        [session addInput:inPut];
    }
    if ([session canAddOutput:outPut]) {
        [session addOutput:outPut];
    }
    
    // 7.告訴輸出對象, 需要輸出什么樣的數據  // 提示:一定要先設置會話的輸出為output之后,再指定輸出的元數據類型!
    [outPut setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];
    
    // 8.創建預覽圖層
    AVCaptureVideoPreviewLayer *preViewLayer = [AVCaptureVideoPreviewLayer layerWithSession:session];
    preViewLayer.frame = self.view.bounds;
    [self.view.layer insertSublayer:preViewLayer atIndex:0];
    
    // 9.設置掃面范圍
    outPut.rectOfInterest = CGRectMake(0.2, 0.18, 0.6, 0.5);
    
    // 10.設置掃描框
    UIView *boxView = [[UIView alloc] initWithFrame:CGRectMake(0.2 * SrceenW, 0.18 * SrceenH, 0.6 * SrceenW, 0.5 * SrceenH)];
    self.boxView = boxView;
    
    
    boxView.layer.borderColor = [UIColor yellowColor].CGColor;
    boxView.layer.borderWidth = 3;
    
    [self.view addSubview:boxView];
    
    // 設置掃描線
    CALayer *scanLayer = [[CALayer alloc] init];
    self.scanLayer = scanLayer;
    
    scanLayer.frame = CGRectMake(0, 0, boxView.bounds.size.width, 2);
    scanLayer.backgroundColor = [UIColor redColor].CGColor;
    [boxView.layer addSublayer:scanLayer];
    
    // 開始掃描
    [session startRunning];

其中第9個步驟是可以優化內存的

@property(nonatomic) CGRect rectOfInterest;

這個屬性大致意思就是告訴系統它需要注意的區域,大部分APP的掃碼UI中都會有一個框,提醒你將條形碼放入那個區域,這個屬性的作用就在這里,它可以設置一個范圍,只處理在這個范圍內捕獲到的圖像的信息。如此一來,我們代碼的效率又會得到很大的提高,在使用這個屬性的時候。需要幾點注意:

1、這個CGRect參數和普通的Rect范圍不太一樣,它的四個值的范圍都是0-1,表示比例。

2、經過測試發現,這個參數里面的x對應的恰恰是距離左上角的垂直距離,y對應的是距離左上角的水平距離。

3、寬度和高度設置的情況也是類似。

 

/// 經過測試  使用rectOfInterest 更改掃描范圍 並沒有很好的可控制范圍,如果想達到想微信那樣,只有在固定的掃描框中才可以掃描成功

    可以使用以下設置,在

-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection; 方法中,判斷二維碼的三個坐標點是否在掃描框中。

 

for (id objects in metadataObjects) {
        // 判斷檢測到的對象類型

        if (![objects isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) {

            return;

        }

        // 轉換對象坐標

        AVMetadataMachineReadableCodeObject *obj = (AVMetadataMachineReadableCodeObject *)[preViewLayer transformedMetadataObjectForMetadataObject:objects];

        // 判斷掃描范圍

        if (!CGRectContainsRect(self.boxView.frame, obj.bounds)) {

            continue;

     }

}

 

 

 

 

-----------------------------以下是源碼:

#import "ScanQrcodeVController.h"

@protocol ScanQrcodeVControllerDelegate <NSObject>
// 二維碼返回結果
-(void)scanQrcodeWithNString:(NSString *) ruselt;
@end
@interface ScanQrcodeVController : UIViewController
@property (nonatomic, weak) id<ScanQrcodeVControllerDelegate>delegate;
@end

#import "ScanQrcodeVController.m"

@interface ScanQrcodeVController ()<AVCaptureMetadataOutputObjectsDelegate>
// 會話
@property (nonatomic, strong) AVCaptureSession *session;
// 定時器
@property (nonatomic, strong) CADisplayLink *link;
// 掃描線
@property (nonatomic, strong) CALayer *scanLayer;
// 掃描框
@property (nonatomic, weak) UIView *boxView;
/// 保存二維碼結果
@property (nonatomic, copy) NSString *string;
@end

@implementation ScanQrcodeVController
- (void)viewDidLoad {
    [super viewDidLoad];

    self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"NavBack"] style:UIBarButtonItemStylePlain target:self action:@selector(goBack)];
    
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"確定" style:UIBarButtonItemStylePlain target:self action:@selector(doneClick)];
    
    [self scanCode];  
}


-(void)scanCode {
    
    CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(updataFrame)];
    self.link = link;
    link.frameInterval = 3;
    
    [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];

  

    // 判斷相機是否授權使用相機

 
          

    AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];

 
          

    if(status == AVAuthorizationStatusAuthorized) {

 
          

    } else if(status == AVAuthorizationStatusDenied){

 
          

       // NSLog(@"denied不允許");

 
          

        return ;

 
          

    } else if(status == AVAuthorizationStatusNotDetermined){

 
          

        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {

 
          

            if(granted){

 
          

//                NSLog(@"允許");

 
          

            } else {

 
          

//                NSLog(@"不允許");

 
          

                return;

 
          

            }

 
          

        }];

 
          

    // 1.獲取輸入設備

 AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; // 2.創建輸入對象 NSError *error; AVCaptureDeviceInput *inPut = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error]; if (inPut == nil) { UIAlertView *aler = [[UIAlertView alloc] initWithTitle:@"提示" message:@"設備不可用" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"確定", nil]; [self.view addSubview:aler]; [aler show]; return; } // 3.創建輸出對象 AVCaptureMetadataOutput *outPut = [[AVCaptureMetadataOutput alloc] init]; // 4.設置代理監聽輸出對象的輸出流 說明:使用主線程隊列,相應比較同步,使用其他隊列,相應不同步,容易讓用戶產生不好的體驗  [outPut setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; // 5.創建會話 AVCaptureSession *session = [[AVCaptureSession alloc] init]; self.session = session; // 6.將輸入和輸出對象添加到會話 if ([session canAddInput:inPut]) { [session addInput:inPut]; } if ([session canAddOutput:outPut]) { [session addOutput:outPut]; } // 7.告訴輸出對象, 需要輸出什么樣的數據 // 提示:一定要先設置會話的輸出為output之后,再指定輸出的元數據類型!  [outPut setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]]; // 8.創建預覽圖層 AVCaptureVideoPreviewLayer *preViewLayer = [AVCaptureVideoPreviewLayer layerWithSession:session]; preViewLayer.frame = self.view.bounds; [self.view.layer insertSublayer:preViewLayer atIndex:0]; // 9.設置掃面范圍 outPut.rectOfInterest = CGRectMake(0.2, 0.18, 0.6, 0.5); // 10.設置掃描框 UIView *boxView = [[UIView alloc] initWithFrame:CGRectMake(0.2 * SrceenW, 0.18 * SrceenH, 0.6 * SrceenW, 0.5 * SrceenH)]; self.boxView = boxView; boxView.layer.borderColor = [UIColor yellowColor].CGColor; boxView.layer.borderWidth = 3; [self.view addSubview:boxView]; // 設置掃描線 CALayer *scanLayer = [[CALayer alloc] init]; self.scanLayer = scanLayer; scanLayer.frame = CGRectMake(0, 0, boxView.bounds.size.width, 2); scanLayer.backgroundColor = [UIColor redColor].CGColor; [boxView.layer addSublayer:scanLayer]; // 開始掃描  [session startRunning]; } -(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
    for (id objects in metadataObjects) {

        // 判斷檢測到的對象類型

        if (![objects isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) {

            return;

        }

        // 轉換對象坐標

        AVMetadataMachineReadableCodeObject *obj = (AVMetadataMachineReadableCodeObject *)[preViewLayer transformedMetadataObjectForMetadataObject:objects];

        // 判斷掃描范圍

        if (!CGRectContainsRect(self.boxView.frame, obj.bounds)) {

            continue;

     }

 
          

        // 設置代理

      if ([self.delegate respondsToSelector:@selector(scanQrcodeWithNString:)]) {

            [self.delegate scanQrcodeWithNString:obj.stringValue];

     } 

     // 停止掃描

     [self.session stopRunning];

        // 移除CADisplayLink對象

        [self.link removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];

        self.link = nil;

    }


} -(void)updataFrame { CGRect frame = self.scanLayer.frame; if (self.scanLayer.frame.origin.y > self.boxView.frame.size.height) { frame.origin.y = -20; self.scanLayer.frame = frame; }else{ frame.origin.y += 3; self.scanLayer.frame = frame; } } -(void)viewDidDisappear:(BOOL)animated{ [super viewDidDisappear:animated]; // 記得釋放CADisplayLink對象 if (self.link != nil) { [self.link invalidate]; self.link = nil; } } // 返回上一個界面 -(void)goBack { [self.navigationController popViewControllerAnimated:YES]; } // 二維碼掃描完成 -(void)doneClick { // 設置代理 if ([self.delegate respondsToSelector:@selector(scanQrcodeWithNString:)]) { [self.delegate scanQrcodeWithNString:self.string]; } [self.navigationController popToRootViewControllerAnimated:YES]; } @end

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM