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代替。

       由于我们的项目还要支持iOS6的用户,所以选择了ZBar。那我们就先从ZBar说起吧。官网介绍 http://zbar.sourceforge.net  SDK下载 http://sourceforge.net/projects/zbar/files/iPhoneSDK/ZBarSDK-1.2.dmg/download 。首先声明一下,官方下载的ZBar SDK竟然是不支持64位的,而苹果早就已经开始对上传到 iTunes 上的应用强制要求支持64位。所以需要的可以从这里下载,链接:http://download.csdn.net/detail/he_jiabin/9043381 已经支持64位的ZBarSDK静态库,同时解决中文乱码的问题。使用可以参考官方的帮助文档:http://zbar.sourceforge.net/iphone/sdkdoc/install.html 。基本的步骤就是导入需要的framework,如下图

IOS几种常见的实现扫描、生成二维码的方式(一、使用ZBar SDK)

        在使用的扫码的控制器中引入#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:^{
    }];
}


常规的扫描效果:

IOS几种常见的实现扫描、生成二维码的方式(一、使用ZBar SDK)

        第二种方式是我们着重要讲的自定义扫描界面的方式。即通过自定义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:^{
    }];
}


自定义的扫描二维码效果:

IOS几种常见的实现扫描、生成二维码的方式(一、使用ZBar SDK)       

        这里通过ZBar SDK自定义扫描二维码的步骤基本完成。 

        接下来,我们来讲一下如何生成一张二维码。在生成二维码的库中QREncoder最为常见,但是由于中文字符的特殊性,生成中文的时候会出现一定的错误,所以采用 libqrencode,二维码生成框架libqrencode是一个纯C编写的类库,支持面也更广泛。

       首先下载libqrencode源码,可以到这里下载http://download.csdn.net/detail/he_jiabin/9111589。在工程中导入相关的framework。已经导入的framework无需再导入。


      将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文件路径。

       如果这两步都配置好了之后,我们就可以正确生成二维码了。

       生成二维码效果:

IOS几种常见的实现扫描、生成二维码的方式(一、使用ZBar SDK)

项目Demo已经放在GitHub,也算是做了一个小结。https://github.com/jaybinhe/JB_ZBarSDK_Demo