1. 程式人生 > >IOS幾種常見的實現掃描、生成二維碼的方式(一、使用ZBar SDK)

IOS幾種常見的實現掃描、生成二維碼的方式(一、使用ZBar SDK)

        最近專案中使用到掃描二維碼的功能,要求還蠻多的,包括介面的定製,掃描靈敏度,使用的穩定性等等。於是,研究總結了一下IOS中幾種掃描二維碼的實現方式。

        基本上我們的實現方法有三大途徑:ZBar、ZXing、AVFoundation。在 iOS7 以前,在iOS中實現二維碼和條形碼掃描,我們知道的有兩大開源元件ZBar與ZXing,而iOS7後蘋果也提供AVFoundation支援二維碼的掃描。

        ZBar在掃描的靈敏度上和記憶體的使用上相對於ZXing上都是較優的,但是對於 “圓角二維碼” 的掃描確很困難。ZXing 是 Google Code上的一個開源的條形碼掃描庫,是用java設計的,連Google Glass 都在使用的。但有人為了追求更高效率以及可移植性,出現了c++ port. Github上的Objectivc-C port,其實就是用OC程式碼封裝了一下而已,而且已經停止維護。

ios7以上AVFoundation提供原生api掃描二維碼,無論在掃描靈敏度和效能上來說都是最優的,所以毫無疑問我們應該切換到AVFoundation,但是如果需要相容iOS 6或之前的版本就要使用ZBar或ZXing代替。


        在使用的掃碼的控制器中引入#import "ZBarSDK.h"標頭檔案,就可以使用了。值得一提的是,通過提供的SDK我們可以看到,ZBar SDK提供了兩種使用方式:1)直接呼叫 ZBarReaderViewController 開啟一個掃描介面;2)ZBar SDK提供的可以嵌入其他檢視的ZBarReaderView(建議使用該方式,我們可以自定義掃描介面,我們後面定製掃描的頁面就是通過這種方式)。

        第一種方式是常規的方式,通過ZBarReaderViewController直接開啟一個掃描介面進行掃描。首先引用標頭檔案 #import"ZBarSDK.h",實現代理<ZBarReaderDelegate

初始化掃描二維碼控制器

- (void)normalScanQRCodeView{
    //初始化掃描二維碼控制器
    ZBarReaderViewController *reader = [ZBarReaderViewController new];
    //設定代理
    reader.readerDelegate = self;
    //基本適配
    reader.supportedOrientationsMask = ZBarOrientationMaskAll;
    //二維碼/條形碼識別設定
    ZBarImageScanner *scanner = reader.scanner;
    [scanner setSymbology: ZBAR_I25
                   config: ZBAR_CFG_ENABLE
                       to: 0];
    //彈出系統照相機,全屏拍攝
    [self presentViewController:reader animated:YES completion:^{
        
    }];
}

掃描二維碼成功、讀取二維碼內容

#pragma mark -
#pragma mark ZBarReaderDelegate
//掃描二維碼的時候,識別成功會進入此方法,讀取二維碼內容
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    id<NSFastEnumeration> results = [info objectForKey:ZBarReaderControllerResults];
    ZBarSymbol * symbol;
    for(symbol in results)
        break;
    
    [picker dismissViewControllerAnimated:YES completion:nil];
    
    NSString *result = symbol.data;
    
    NSLog(@"%@",result);
    
    //二維碼掃描成功,彈窗提示
    UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"掃描成功" message:[NSString stringWithFormat:@"二維碼內容:\n%@",result] preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *action = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
    }];
    [alertVC addAction:action];
    [self presentViewController:alertVC animated:YES completion:^{
    }];
}

常規的掃描效果


        第二種方式是我們著重要講的自定義掃描介面的方式。即通過自定義ZBarReaderView的介面達到自定義的效果。首先引用標頭檔案 #import"ZBarSDK.h",實現代理<ZBarReaderViewDelegate>

初始化掃描二維碼物件ZBarReaderView

/**
 *初始化掃描二維碼物件ZBarReaderView
 *@param 設定掃描二維碼檢視的窗口布局、引數
 */
-(void)configuredZBarReader{
    //初始化照相機視窗
    _readview = [[ZBarReaderView alloc] init];
    //設定掃描代理
    _readview.readerDelegate = self;
    //關閉閃光燈
    _readview.torchMode = 0;
    //顯示幀率
    _readview.showsFPS = NO;
    //將其照相機拍攝檢視新增到要顯示的檢視上
    [self.view addSubview:_readview];
    //二維碼/條形碼識別設定
    ZBarImageScanner *scanner = _readview.scanner;
    [scanner setSymbology: ZBAR_I25
                   config: ZBAR_CFG_ENABLE
                       to: 0];
    //Layout ZBarReaderView
    __weak __typeof(self) weakSelf = self;
    [_readview mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(weakSelf.view).with.offset(0);
        make.left.equalTo(weakSelf.view).with.offset(0);
        make.right.equalTo(weakSelf.view).with.offset(0);
        make.bottom.equalTo(weakSelf.view).with.offset(0);
    }];
    
    //初始化掃描二維碼檢視的子控制元件
    [self configuredZBarReaderMaskView];
    
    //啟動,必須啟動後,手機攝影頭拍攝的即時影象菜可以顯示在readview上
    [_readview start];
    [_qrRectView startScan];
}

自定義掃描二維碼檢視樣式

/**
 *自定義掃描二維碼檢視樣式
 *@param 初始化掃描二維碼檢視的子控制元件
 */
- (void)configuredZBarReaderMaskView{
    //掃描的矩形方框檢視
    _qrRectView = [[QRView alloc] init];
    _qrRectView.transparentArea = CGSizeMake(220, 220);
    _qrRectView.backgroundColor = [UIColor clearColor];
    [_readview addSubview:_qrRectView];
    [_qrRectView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(_readview).with.offset(0);
        make.left.equalTo(_readview).with.offset(0);
        make.right.equalTo(_readview).with.offset(0);
        make.bottom.equalTo(_readview).with.offset(0);
    }];
    
    //照明按鈕
    _lightingBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    [_lightingBtn setTitle:@"照明" forState:UIControlStateNormal];
    [_lightingBtn.titleLabel setFont:[UIFont systemFontOfSize:14]];
    _lightingBtn.layer.borderColor = [UIColor colorWithHexString:@"#3498db"].CGColor;
    _lightingBtn.layer.borderWidth = 1.0;
    _lightingBtn.layer.cornerRadius = 8.0;
    [_lightingBtn setTitleEdgeInsets:UIEdgeInsetsMake(0, 20, 0, 0)];
    [_lightingBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    [_lightingBtn setBackgroundColor:[UIColor clearColor]];
    _lightingBtn.tag = LIGHTBUTTONTAG;
    [_lightingBtn addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
    [_qrRectView addSubview:_lightingBtn];
    [_lightingBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.equalTo(_qrRectView).with.offset(-100);
        make.centerX.equalTo(_qrRectView);
        make.size.mas_equalTo(CGSizeMake(88, 28));
    }];
    UIImageView *lightImage = [[UIImageView alloc] init];
    lightImage.image = [UIImage imageNamed:@"bulb"];
    [_lightingBtn addSubview:lightImage];
    [lightImage mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerY.equalTo(_lightingBtn);
        make.left.equalTo(_lightingBtn).with.offset(17);
        make.width.equalTo(@22);
        make.height.equalTo(@22);
    }];
    
    //匯入二維碼圖片
    _importQRCodeImageBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    [_importQRCodeImageBtn setTitle:@"匯入二維碼" forState:UIControlStateNormal];
    [_importQRCodeImageBtn.titleLabel setFont:[UIFont systemFontOfSize:12]];
    [_importQRCodeImageBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    [_importQRCodeImageBtn setBackgroundColor:[UIColor clearColor]];
    _importQRCodeImageBtn.tag = IMPORTBUTTONTAG;
    [_importQRCodeImageBtn addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
    [_qrRectView addSubview:_importQRCodeImageBtn];
    [_importQRCodeImageBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.equalTo(_qrRectView.mas_bottom).with.offset(-32);
        make.right.equalTo(_qrRectView.mas_right).with.offset(-20);
        make.size.mas_equalTo(CGSizeMake(60, 12));
    }];
    
    _importQRCodeImage = [UIButton buttonWithType:UIButtonTypeCustom];
    [_importQRCodeImage setBackgroundImage:[UIImage imageNamed:@"album"] forState:UIControlStateNormal];
    _importQRCodeImage.tag = IMPORTBUTTONTAG;
    [_importQRCodeImage addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
    [_qrRectView addSubview:_importQRCodeImage];
    [_importQRCodeImage mas_makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.equalTo(_qrRectView).with.offset(-48);
        make.centerX.equalTo(_importQRCodeImageBtn);
        make.size.mas_equalTo(CGSizeMake(32, 32));
    }];
}

控制元件事件處理

- (void)buttonClicked:(UIButton *)sender{
    switch (sender.tag) {
        case LIGHTBUTTONTAG://照明按鈕
        {
            if(0 != _readview.torchMode){
                //關閉閃光燈
                _readview.torchMode = 0;
            }else if (0 == _readview.torchMode){
                //開啟閃光燈
                _readview.torchMode = 1;
            }
            
        }
            break;
        case IMPORTBUTTONTAG://匯入二維碼圖片
        {
            [self presentImagePickerController];
        }
            break;
    
        default:
            break;
    }
}

/**
 *開啟二維碼掃描檢視ZBarReaderView
 *@param 關閉閃光燈
 */
- (void)setZBarReaderViewStart{
    _readview.torchMode = 0;//關閉閃光燈
    [_readview start];//開始掃描二維碼
    [_qrRectView startScan];
    
}

/**
 *關閉二維碼掃描檢視ZBarReaderView
 *@param 關閉閃光燈
 */
- (void)setZBarReaderViewStop{
    _readview.torchMode = 0;//關閉閃光燈
    [_readview stop];//關閉掃描二維碼
    [_qrRectView stopScan];
}

//彈出系統相簿、相機
-(void)presentImagePickerController{
    UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    _picker = [[UIImagePickerController alloc] init];
    _picker.sourceType               = sourceType;
    _picker.allowsEditing            = YES;
    //    NSArray *temp_MediaTypes        = [UIImagePickerController availableMediaTypesForSourceType:picker.sourceType];
    //    picker.mediaTypes               = temp_MediaTypes;
    _picker.delegate                 = self;
    
    UIWindow *window = [UIApplication sharedApplication].keyWindow;
    [window addSubview:_picker.view];
    [_picker.view mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(window);
        make.size.equalTo(window);
    }];
}

-(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker{
    //收起相簿
    [picker.view removeFromSuperview];
}

最關鍵的步驟就是掃描二維碼獲取二維碼內容和從相簿匯入二維碼圖片獲取二維碼內容

#pragma mark -
#pragma mark ZBarReaderViewDelegate
//掃描二維碼的時候,識別成功會進入此方法,讀取二維碼內容
- (void) readerView: (ZBarReaderView*) readerView
     didReadSymbols: (ZBarSymbolSet*) symbols
          fromImage: (UIImage*) image{
    //停止掃描
    [self setZBarReaderViewStop];
    
    ZBarSymbol *symbol = nil;
    for (symbol in symbols) {
        break;
    }
    NSString *urlStr = symbol.data;
    
    if(urlStr==nil || urlStr.length<=0){
        //二維碼內容解析失敗
        UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"掃描失敗" message:nil preferredStyle:UIAlertControllerStyleAlert];
        __weak __typeof(self) weakSelf = self;
        UIAlertAction *action = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
            //重新掃描
            [weakSelf setZBarReaderViewStart];
        }];
        [alertVC addAction:action];
        [self presentViewController:alertVC animated:YES completion:^{
        }];
        
        return;
    }
    
    NSLog(@"urlStr: %@",urlStr);
    
    //二維碼掃描成功,彈窗提示
    UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"掃描成功" message:[NSString stringWithFormat:@"二維碼內容:\n%@",urlStr] preferredStyle:UIAlertControllerStyleAlert];
    __weak __typeof(self) weakSelf = self;
    UIAlertAction *action = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
        //繼續掃描
        [weakSelf setZBarReaderViewStart];
    }];
    [alertVC addAction:action];
    [self presentViewController:alertVC animated:YES completion:^{
        
    }];
    
}

#pragma mark -
#pragma mark UIImagePickerControllerDelegate
//匯入二維碼的時候會進入此方法,處理選中的相片獲取二維碼內容
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
    //停止掃描
    [self setZBarReaderViewStop];
    
    //處理選中的相片,獲得二維碼裡面的內容
    ZBarReaderController *reader = [[ZBarReaderController alloc] init];
    UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
    CGImageRef cgimage = image.CGImage;
    ZBarSymbol *symbol = nil;
    for(symbol in [reader scanImage:cgimage])
        break;
    NSString *urlStr = symbol.data;
    
    [picker.view removeFromSuperview];
    
    if(urlStr==nil || urlStr.length<=0){
        //二維碼內容解析失敗
        UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"掃描失敗" message:nil preferredStyle:UIAlertControllerStyleAlert];
        __weak __typeof(self) weakSelf = self;
        UIAlertAction *action = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
            //重新掃描
            [weakSelf setZBarReaderViewStart];
        }];
        [alertVC addAction:action];
        [self presentViewController:alertVC animated:YES completion:^{
        }];

        return;
    }
    
    NSLog(@"urlStr: %@",urlStr);
    
    //二維碼掃描成功,彈窗提示
    UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"掃描成功" message:[NSString stringWithFormat:@"二維碼內容:\n%@",urlStr] preferredStyle:UIAlertControllerStyleAlert];
    __weak __typeof(self) weakSelf = self;
    UIAlertAction *action = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
        //繼續掃描
        [weakSelf setZBarReaderViewStart];
    }];
    [alertVC addAction:action];
    [self presentViewController:alertVC animated:YES completion:^{
    }];
}

自定義的掃描二維碼效果:


        這裡通過ZBar SDK自定義掃描二維碼的步驟基本完成。 

        接下來,我們來講一下如何生成一張二維碼。在生成二維碼的庫中QREncoder最為常見,但是由於中文字元的特殊性,生成中文的時候會出現一定的錯誤,所以採用 libqrencode,二維碼生成框架libqrencode是一個純C編寫的類庫,支援面也更廣泛。



將libqrencode原始碼加入工程後,在需要使用生成二維碼的控制器中引用標頭檔案 #import"QRCodeGenerator.h" 

生成二維碼圖片的方法如下:

testImageView.image = [QRCodeGenerator qrImageForString:testTextField.text imageSize:testImageView.bounds.size.width];

       到這裡,看來生成二維碼圖片的步驟確實很簡單。但是在我們將libqrencode原始碼加入工程後,在相關控制器引用標頭檔案 #import"QRCodeGenerator.h"的時候,編譯卻報錯了。認真一看還是一大片的紅色錯誤。這是因為,在使用libqrencode框架的時候,需要我們對專案做一些必要的配置修改。

       第一步:需要我們去手動修改.pch檔案。需要在.pch檔案中新增以下內容:

#import <Availability.h>

#ifndef __IPHONE_4_0
#warning "This project uses features only available in iOS SDK 4.0 and later."
#endif

#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#endif

       貌似現在Xcode6+以後,Xcode生成的專案預設都是不帶.pch檔案的。這個時候需要我們自己生成.pch檔案,然後修改裡面的內容。生成.pch檔案步驟:Xcode-->File-->New-->File-->Other-->PCH File


       第二步:配置好相應的Prefix Header檔案路徑。

       如果這兩步都配置好了之後,我們就可以正確生成二維碼了。

       生成二維碼效果: