博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS - QRCode 二维码
阅读量:6275 次
发布时间:2019-06-22

本文共 20012 字,大约阅读时间需要 66 分钟。

1、QRCode

  • 在 iOS7 以前,在 iOS 中实现二维码和条形码扫描,我们所知的有,两大开源组件 ZBar 与 ZXing。iOS7 之后可以利用系统原生 API 生成二维码, iOS8 之后可以生成条形码, 系统默认生成的颜色是黑色。

    • 1、ZBar 在扫描的灵敏度上,和内存的使用上相对于 ZXing 上都是较优的,但是对于 “圆角二维码” 的扫描确很困难。
    • 2、ZXing 是 Google Code 上的一个开源的条形码扫描库,是用 java 设计的,连 Google Glass 都在使用的。但有人为了追求更高效率以及可移植性,出现了 c++ port。Github 上的 Objectivc-C port,其实就是用 OC 代码封装了一下而已,而且已经停止维护。这样效率非常低,在 instrument 下面可以看到 CPU 和内存疯涨,在内存小的机器上很容易崩溃。
    • 3、AVFoundation 无论在扫描灵敏度和性能上来说都是最优的,所以毫无疑问我们应该切换到 AVFoundation,需要兼容 iOS 6 或之前的版本可以用 ZBar 或 ZXing 代替。
  • 在 iOS8 + 系统中使用相机需要在 Info.plist 中添加 Privacy - Camera Usage Description,并设置其值。使用相册需要在 Info.plist 中添加 Privacy - Photo Library Usage Description,并设置其值。

    QRCode24

  • 按照下图在 Info.plist 文件中将 Localization native development region 的值改为 China。如果不设置此项弹出的相册页面中显示的按钮等为英文菜单。

    QRCode25

2、系统原生二维码

2.1 扫描二维码

  • 官方提供的接口非常简单,直接看代码,主要使用的是 AVFoundation。

    // 包含头文件    #import 
    // 遵守协议
    // 输入输出的中间桥梁 @property (nonatomic, strong) AVCaptureSession *session; // 扫描窗口 @property (nonatomic, strong) UIImageView *scanView; // 创建扫描视图窗口,自定义方法 - (void)createdScanView { CGFloat margin = 50; CGRect scanFrame = CGRectMake(margin, margin + 20, self.view.bounds.size.width - margin * 2, self.view.bounds.size.width - margin * 2); self.scanView = [[UIImageView alloc] initWithFrame:scanFrame]; self.scanView.image = [UIImage imageNamed:@"scan_bg2"]; [self.view addSubview:self.scanView]; // 创建扫描 [self startScan]; } // 创建扫描,自定义方法 - (void)startScan { // 获取摄像设备 AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; // 创建输入流 AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil]; // 创建输出流 AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init]; // 设置代理,在主线程里刷新 [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; // 设置有效扫描区域 output.rectOfInterest = [self getScanCropWithScanViewFrame:self.scanView.frame readerViewBounds:self.view.bounds]; // 初始化链接对象 self.session = [[AVCaptureSession alloc] init]; // 设置采集率,高质量 [self.session setSessionPreset:AVCaptureSessionPresetHigh]; [self.session addInput:input]; [self.session addOutput:output]; // 设置扫码支持的编码格式(如下设置条形码和二维码兼容) output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code]; AVCaptureVideoPreviewLayer *layer = [AVCaptureVideoPreviewLayer layerWithSession:self.session]; layer.videoGravity = AVLayerVideoGravityResizeAspectFill; layer.frame = self.view.layer.bounds; [self.view.layer insertSublayer:layer atIndex:0]; // 开始捕获 [self.session startRunning]; } // 设置扫描区域的比例关系,自定义方法 - (CGRect)getScanCropWithScanViewFrame:(CGRect)scanViewFrame readerViewBounds:(CGRect)readerViewBounds { CGFloat x, y, width, height; x = scanViewFrame.origin.y / readerViewBounds.size.height; y = scanViewFrame.origin.x / readerViewBounds.size.width; width = scanViewFrame.size.height / readerViewBounds.size.height; height = scanViewFrame.size.width / readerViewBounds.size.width; return CGRectMake(x, y, width, height); } // 获取扫描结果,AVCaptureMetadataOutputObjectsDelegate 协议方法 - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection { if (metadataObjects.count > 0) { // 停止扫描 [self.session stopRunning]; AVMetadataMachineReadableCodeObject *metadataObject = [metadataObjects objectAtIndex:0]; // 获取扫描结果 NSString *resultString = metadataObject.stringValue; // 输出扫描字符串 [[[UIAlertView alloc] initWithTitle:@"扫描成功" message:resultString delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil] show]; } }
  • 一些初始化的代码加上实现代理方法便完成了二维码扫描的工作,这里我们需要注意的是,在二维码扫描的时候,我们一般都会在屏幕中间放一个方框,用来显示二维码扫描的大小区间,这里我们在AVCaptureMetadataOutput 类中有一个 rectOfInterest 属性,它的作用就是设置扫描范围。这个 CGRect 参数和普通的 Rect 范围不太一样,它的四个值的范围都是 0-1,表示比例。rectOfInterest 都是按照横屏来计算的,所以当竖屏的情况下 x 轴和 y 轴要交换一下,宽度和高度设置的情况也是类似。

  • 效果

    QRCode10QRCode11

2.2 读取二维码

  • 读取主要用到 CoreImage 不过要强调的是读取二维码的功能只有在 iOS8 之后才支持,我们需要在相册中调用一个二维码,将其读取,代码如下

    // 遵守协议    
    // 打开相册,选取图片,自定义方法 - (void)readQRCode { if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) { // 初始化相册拾取器 UIImagePickerController *picker = [[UIImagePickerController alloc] init]; // 设置资源 picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; // 设置代理 picker.delegate = self; [self presentViewController:picker animated:YES completion:nil]; } else { NSString *errorStr = [NSString stringWithFormat:@"请在系统设置->隐私->照片中允许 \"%@\" 使用照片。", [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleNameKey]]; [[[UIAlertView alloc] initWithTitle:@"读取失败" message:errorStr delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil] show]; } } // 获取选中的图片,imagePickerController 协议方法 - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { // 获取选择的图片 UIImage *image = info[UIImagePickerControllerOriginalImage]; // 识别图片中的二维码 NSString *resultString = [self recognizeQRCodeFromImage:image]; // 输出扫描字符串 [[[UIAlertView alloc] initWithTitle:@"读取成功" message:resultString delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil] show]; // 返回 [picker dismissViewControllerAnimated:YES completion:nil]; } // 识别图片中的二维码 - (NSString *)recognizeQRCodeFromImage:(UIImage *)image { CIImage *ciImage = [CIImage imageWithCGImage:image.CGImage]; // 初始化扫描仪,设置识别类型和识别质量 CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}]; // 扫描获取的特征组 NSArray *features = [detector featuresInImage:ciImage]; if (features.count >= 1) { // 获取扫描结果 CIQRCodeFeature *feature = [features objectAtIndex:0]; NSString *resultString = feature.messageString; return resultString; } else { return @"该图片不包含二维码"; } }
  • 效果

    QRCode12QRCode13

2.3 长按识别二维码

  • 这个功能有很多的地方在用,最让人熟知的我想便是微信了,其实实现方法还是很简单的。

    // 创建图片,添加长按手势    - (void)recognizeQRCode {        CGFloat margin = 50;        CGRect scanFrame = CGRectMake(margin,                                      margin + 20,                                      self.view.bounds.size.width - margin * 2,                                      self.view.bounds.size.width - margin * 2);        UIImageView *imageView = [[UIImageView alloc] initWithFrame:scanFrame];        imageView.image = [UIImage imageNamed:@"demo"];        imageView.userInteractionEnabled = YES;        [self.view addSubview:imageView];        UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self                                                                                                 action:@selector(dealLongPress:)];        [imageView addGestureRecognizer:longPress];    }    // 处理长按手势,识别图片中的二维码    - (void)dealLongPress:(UIGestureRecognizer *)gesture {        if (gesture.state == UIGestureRecognizerStateBegan) {            UIImageView *pressedImageView = (UIImageView *)gesture.view;            if (pressedImageView.image) {                // 识别图片中的二维码                NSString *resultString = [self recognizeQRCodeFromImage:pressedImageView.image];                // 输出扫描字符串                [[[UIAlertView alloc] initWithTitle:@"识别成功"                                            message:resultString                                           delegate:nil                                  cancelButtonTitle:@"确定"                                  otherButtonTitles:nil] show];            } else {                [[[UIAlertView alloc] initWithTitle:@"识别失败"                                            message:@"该图片不包含二维码"                                           delegate:nil                                  cancelButtonTitle:@"确定"                                  otherButtonTitles:nil] show];            }        }    }    // 识别图片中的二维码    - (NSString *)recognizeQRCodeFromImage:(UIImage *)image {        CIImage *ciImage = [CIImage imageWithCGImage:image.CGImage];        // 初始化扫描仪,设置识别类型和识别质量        CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode                                                  context:nil                                                  options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}];        // 扫描获取的特征组        NSArray *features = [detector featuresInImage:ciImage];        if (features.count >= 1) {            // 获取扫描结果            CIQRCodeFeature *feature = [features objectAtIndex:0];            NSString *resultString = feature.messageString;            return resultString;        } else {            return @"该图片不包含二维码";        }    }
  • 效果

    QRCode14QRCode15

2.4 生成二维码

  • 生成二维码,其实也是用到 CoreImage,但是步骤繁琐一些,代码如下

    // 创建 ImageView,存放生成的二维码    - (void)createQRCode {        CGFloat margin = 50;        CGRect frame = CGRectMake(margin,                                  margin + 20,                                  self.view.bounds.size.width - margin * 2,                                  self.view.bounds.size.width - margin * 2);        UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];        [self.view addSubview:imageView];        // 生成二维码        [self createQRCodeToImageView:imageView                           fromString:@"qianchia"                             withIcon:[UIImage imageNamed:@"demo1"]                            withColor:[UIColor redColor]];    }    // 生成二维码    - (void)createQRCodeToImageView:(UIImageView *)imageView                         fromString:(NSString *)inputString                           withIcon:(UIImage *)icon                          withColor:(UIColor *)color {        // 创建过滤器        CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];        // 恢复默认        [filter setDefaults];        // 给过滤器添加数据        NSData *data = [inputString dataUsingEncoding:NSUTF8StringEncoding];        // 通过 KVO 设置滤镜 inputMessage 数据        [filter setValue:data forKey:@"inputMessage"];        // 设置二维码颜色        UIColor *onColor = color ? : [UIColor blackColor];        UIColor *offColor = [UIColor whiteColor];        CIFilter *colorFilter = [CIFilter filterWithName:@"CIFalseColor"                                           keysAndValues:@"inputImage", filter.outputImage,                                                         @"inputColor0", [CIColor colorWithCGColor:onColor.CGColor],                                                         @"inputColor1", [CIColor colorWithCGColor:offColor.CGColor],                                                         nil];        // 获取输出的二维码        CIImage *outputImage = colorFilter.outputImage;        // CIImage *outputImage = [filter outputImage];        CIContext *context = [CIContext contextWithOptions:nil];        CGImageRef cgImage = [context createCGImage:outputImage                                           fromRect:[outputImage extent]];        // 将 CIImage 转换成 UIImage,并放大显示        UIImage *qrImage = [UIImage imageWithCGImage:cgImage                                             scale:1.0                                       orientation:UIImageOrientationUp];        // 重绘 UIImage,默认情况下生成的图片比较模糊        CGFloat scale = 100;        CGFloat width = qrImage.size.width * scale;        CGFloat height = qrImage.size.height * scale;        UIGraphicsBeginImageContext(CGSizeMake(width, height));        CGContextRef context1 = UIGraphicsGetCurrentContext();        CGContextSetInterpolationQuality(context1, kCGInterpolationNone);        [qrImage drawInRect:CGRectMake(0, 0, width, height)];        qrImage = UIGraphicsGetImageFromCurrentImageContext();        UIGraphicsEndImageContext();        CGImageRelease(cgImage);        // 添加头像        if (icon) {            UIGraphicsBeginImageContext(qrImage.size);            [qrImage drawInRect:CGRectMake(0, 0, qrImage.size.width, qrImage.size.height)];            // 设置头像大小            CGFloat scale = 5;            CGFloat width = qrImage.size.width / scale;            CGFloat height = qrImage.size.height / scale;            CGFloat x = (qrImage.size.width - width) / 2;            CGFloat y = (qrImage.size.height - height) / 2;            [icon drawInRect:CGRectMake( x,  y, width, height)];            UIImage *newImage =  UIGraphicsGetImageFromCurrentImageContext();            UIGraphicsEndImageContext();            imageView.image = newImage;        } else {            imageView.image = qrImage;        }    }
  • 效果

    QRCode16QRCode17

2.5 对原生二维码的封装

  • 具体实现代码见 GitHub 源码

    // 包含头文件    #import "QExtension.h"
  • 扫描/识别二维码

    // 创建二维码扫描视图控制器    QQRCode *qrCode = [QQRCode q_qrCodeWithResult:^(BOOL isSucceed, NSString *result) {        if (isSucceed) {            [[[UIAlertView alloc] initWithTitle:@"Succeed"                                        message:result                                       delegate:nil                              cancelButtonTitle:@"确定"                              otherButtonTitles:nil] show];        } else {            [[[UIAlertView alloc] initWithTitle:@"Failed"                                        message:result                                       delegate:nil                              cancelButtonTitle:@"确定"                              otherButtonTitles:nil] show];        }    }];    // 设置我的二维码信息    qrCode.myQRCodeInfo = @"http://weixin.qq.com/r/xUqbg1-ENgJJrRvg9x-X";    qrCode.headIcon = [UIImage imageNamed:@"demo6"];    // 打开扫描视图控制器    [self presentViewController:qrCode animated:YES completion:nil];
    • 效果

      QRCode18QRCode19

      QRCode20QRCode21

  • 识别二维码

    // 创建图片,添加长按手势    self.imageView.image = [UIImage imageNamed:@"demo4"];    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(dealLongPress:)];    [self.imageView addGestureRecognizer:longPress];    // 处理长按手势,识别图片中的二维码    - (void)dealLongPress:(UIGestureRecognizer *)gesture {        if (gesture.state == UIGestureRecognizerStateBegan) {            UIImageView *pressedImageView = (UIImageView *)gesture.view;            UIImage *image = pressedImageView.image;            // 识别图片中的二维码            NSString *result = [image q_stringByRecognizeQRCode];            [[[UIAlertView alloc] initWithTitle:@"Succeed"                                        message:result                                       delegate:nil                              cancelButtonTitle:@"确定"                              otherButtonTitles:nil] show];        }    }
    • 效果

      QRCode23

  • 生成二维码

    // 生成普通的二维码    UIImage *qrImage = [UIImage q_imageWithQRCodeFromString:@"http://weixin.qq.com/r/xUqbg1-ENgJJrRvg9x-X"                                                   headIcon:nil                                                      color:nil                                                  backColor:nil];    // 生成带头像的二维码    UIImage *qrImage = [UIImage q_imageWithQRCodeFromString:@"http://weixin.qq.com/r/xUqbg1-ENgJJrRvg9x-X"                                                   headIcon:[UIImage imageNamed:@"demo6"]                                                      color:[UIColor blackColor]                                                  backColor:[UIColor whiteColor]];    // 生成指定图片大小的二维码    UIImage *qrImage = [UIImage q_imageWithQRCodeFromString:@"http://weixin.qq.com/r/xUqbg1-ENgJJrRvg9x-X"                                                  imageSize:CGSizeMake(2048, 2048)                                                   headIcon:[UIImage imageNamed:@"demo6"]                                                  headFrame:CGRectMake(819, 819, 410, 410)                                                      color:nil                                                  backColor:nil];
    • 效果

      QRCode28QRCode22

2.6 对原生条形码的封装

  • 具体实现代码见 GitHub 源码

    // 包含头文件    #import "QExtension.h"
  • 扫描条形码

    • 同上面 2.5 的 "扫描/识别二维码"。
  • 生成条形码

    // 生成条形码    UIImage *qrImage = [UIImage q_imageWithBarCodeFromString:@"cnblogs: QianChia"                                                       color:nil                                                   backColor:nil];    // 生成指定图片大小的条形码    UIImage *qrImage = [UIImage q_imageWithBarCodeFromString:@"cnblogs: QianChia"                                                   imageSize:CGSizeMake(1024, 512)                                                       color:[UIColor blueColor]                                                   backColor:[UIColor redColor]];
    • 效果

      QRCode26QRCode27

3、ZBarSDK 二维码使用

  • 使用 ZBarSDK,可以扫描条形码和二维码。

  • 配置

    • 1、添加 SDK 的依赖库和框架。在 项目设置 => TARGETS => Build Phases => Link Binary With Libraries 中依次添加以下库或框架:

      AVFoundation.framework    CoreMedia.framework    CoreVideo.framework    QuartzCore.framework    libiconv.tbd

      QRCode1

    • 2、由于 ZBarSDK 不支持 Bitcode,需要在 Xcode 的项目设置 => TARGETS => Build Settings => Build Options => Enable Bitcode 的值设置为 NO,否则无法进行真机调试。

      QRCode2

    • 3、在 iOS8 + 系统中使用相机需要在 Info.plist 中添加 Privacy - Camera Usage Description,并设置其值。

      QRCode3

    • 4、在需要使用 ZBarSDK 的文件中

      // 包含头文件    #import "ZBarSDK.h"    // 遵守协议    
  • 创建扫描

    // 声明扫描视图    @property (nonatomic, strong) ZBarReaderView *scanView;    // 实例化扫描视图    self.scanView = [[ZBarReaderView alloc] init];    // 设置扫描视图的位置尺寸    float width = self.view.bounds.size.width - 40;    self.scanView.frame = CGRectMake(20, 30, width, width);    // 设置代理人    self.scanView.readerDelegate = self;    // 添加扫描视图    [self.view addSubview:self.scanView];    // 关闭闪光灯    self.scanView.torchMode = 0;    // 开始扫描    [self.scanView start];
  • 获取扫描结果

    // ZBarReaderViewDelegate 协议方法    - (void)readerView:(ZBarReaderView *)readerView didReadSymbols:(ZBarSymbolSet *)symbols fromImage:(UIImage *)image {        for (ZBarSymbol *symbol in symbols) {            // 获取扫描结果            NSString *scanString = symbol.data;            // 显示扫描结果            self.scanResult.text = scanString;        }        // 停止扫描        [self.scanView stop];    }
  • 效果

    QRCode6QRCode7

4、ZCZBarSDK 二维码使用

  • 使用 ,可以扫描条形码和二维码。

  • 配置

    • 1、添加 SDK 的依赖库和框架。在 项目设置 => TARGETS => Build Phases => Link Binary With Libraries 中依次添加以下库或框架:

      libiconv.tbd

      QRCode4

    • 2、由于 ZCZBarSDK 不支持 Bitcode,需要在 Xcode 的项目设置 => TARGETS => Build Settings => Build Options => Enable Bitcode 的值设置为 NO,否则无法进行真机调试。

      QRCode2

    • 3、在 iOS8 + 系统中使用相机需要在 Info.plist 中添加 Privacy - Camera Usage Description,并设置其值。使用相册需要在 Info.plist 中添加 Privacy - Photo Library Usage Description,并设置其值。

      QRCode5

    • 4、在需要使用 ZCZBarSDK 的文件中

      // 包含头文件    #import "ZCZBarViewController.h"
  • 生成二维码

    // 将字符串 self.detailTF.text 中的内容生成的二维码存放到 ImageView(self.codeImageView)中。    if (self.detailTF.text.length != 0) {        [ZCZBarViewController createImageWithImageView:self.codeImageView String:self.detailTF.text Scale:100];    }
  • 扫描二维码

    // isQRCode: 是否关闭条形码扫描,专门扫描二维码。    ZCZBarViewController *zzvc = [[ZCZBarViewController alloc] initWithIsQRCode:NO Block:^(NSString *result, BOOL isSucceed) {        if (isSucceed) {            self.scanResult.text = [NSString stringWithFormat:@"%@", result];        }    }];    // 打开扫描视图控制器    [self presentViewController:zzvc animated:YES completion:nil];
  • 效果

    QRCode8QRCode9

转载地址:http://ccwva.baihongyu.com/

你可能感兴趣的文章
Thinkphp中field和getField
查看>>
AngularJS之初级Route【一】(六)
查看>>
QTP的那些事--采用DOM,描述性编程获取指定的对象
查看>>
linux异步通信之epoll【转】
查看>>
前端自学路线之js篇
查看>>
C++:运算符重载函数之友元运算符重载
查看>>
ANT task之Junit、JunitReport
查看>>
selenium的那些事--运行报错
查看>>
谋求职业发展,是“走”还是“留”
查看>>
SpreadJS 在 Angular2 中支持绑定哪些属性?
查看>>
Lucene 定义
查看>>
硅谷返乡大潮,AI海归大调查:薪酬、发展、故乡情
查看>>
GNU make manual 翻译(八十二)
查看>>
算法学习之顺序结构
查看>>
百度2013年校园招聘题
查看>>
第3章 敏捷项目管理概述
查看>>
设计模式系列-装饰模式
查看>>
Oozie的作用
查看>>
Android -- TabHost、Fragment、状态保存、通信
查看>>
TFS二次开发系列:四、TFS二次开发WorkItem添加和修改、保存
查看>>