转自:http://blog.csdn.net/baidu_31071595/article/details/50878410

同类型文章:http://www.2cto.com/kf/201603/495818.html

http://blog.csdn.net/baidu_31071595/article/details/50878410

http://blog.csdn.net/luco2008/article/details/51207866

前言


二维码每个人都一定不陌生 尤其是在支付宝和微信的努力下 相信每个人都或多或少的使用过"扫一扫"这个功能
而作为开发者 大家都应该有过二维码的开发经验 不管是练习还是研究 或者是公司的项目(尤其是社交类的APP 基本都有扫一扫加好友这个功能吧?)

介绍


说到二维码 其实我们现在生活中接触到的都是QRCode --由日本人在上个世纪末用于汽车工业的一项发明 如今在移动互联网发光发亮
而实际上 除了QRCode 还有很多二维码的类型 最出名的应该就是DataMatrix了 而在4,5年前 一度还有很多QRCode的竞争对手出现 比如微软推出的Microsoft Tag 还有个人觉得很有意思的SnapTag 可是在这几年的发展过程中 还是QRCode笑到了最后 所以现在我们一般提起二维码 就指的是QRCode 下面我们就用QRCode来代替二维码

对于开发人员来说 熟悉的第三方QRCode库有:

  • ZXing
    Google出品并开源 一直到现在都还有专人维护 是世界上使用最广的二维码库 iOS上比较稳定的移植版是ZXingObj

  • ZBar
    功能上与ZXing不相伯仲 可惜的是项目在2012年之后就不维护了 虽然代码到现在还可以使用

而我从开始到现在一直是ZXing的忠实用户 除了Google的光环之外 那时候的ZXing资料也比ZBar多很多 不过随着iOS7的发布 苹果推出了内建的二维码扫描功能 这让这些第三方库多少显得有点尴尬了

回到今天的主题上 为什么说要跟ZXing说再见呢? 其实我目前做的项目使用的也是ZXing 不过在开发过程中发现了一些很严重而且绕不过去的问题 比如下面这张图

此处输入图片的描述

如果使用相册读取QRCode的功能 ZXing是无法识别的 不信大家可以去zxing.org 我也去Github提了issue 不过作者也没有给出有效的解决办法 所以只能跟ZXing说88了(不过ZBar是可以扫描出来的哟!)

先分析一下 我们使用QRCode一般都是要做如下几种场景的操作

  • 扫描
    比如 微信的"扫一扫"加好友
  • 读取
    比如 微信中长按图片 会弹出"识别图中二维码"这个功能
  • 生成
    比如 微信的"我的二维码"功能

接下来我们就分三个部分分别来介绍一下如何实现对应的功能(要注意的是 "读取图片"这个功能只有在iOS8以上的系统才能支持 iOS7的话 建议还是使用ZXing或者ZBar)

扫描


扫描主要使用的是AVFoundation 使用起来也非常的简单 正常的初始化流程如下

@interface Example1Controller()
<
AVCaptureMetadataOutputObjectsDelegate,
UIAlertViewDelegate
>@property (nonatomic, strong) UIView *scanRectView;@property (strong, nonatomic) AVCaptureDevice            *device;
@property (strong, nonatomic) AVCaptureDeviceInput       *input;
@property (strong, nonatomic) AVCaptureMetadataOutput    *output;
@property (strong, nonatomic) AVCaptureSession           *session;
@property (strong, nonatomic) AVCaptureVideoPreviewLayer *preview;@end@implementation Example1Controller- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view.self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];self.input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:nil];self.output = [[AVCaptureMetadataOutput alloc]init];[self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];self.session = [[AVCaptureSession alloc]init];[self.session setSessionPreset:([UIScreen mainScreen].bounds.size.height<500)?AVCaptureSessionPreset640x480:AVCaptureSessionPresetHigh];[self.session addInput:self.input];[self.session addOutput:self.output];self.output.metadataObjectTypes=@[AVMetadataObjectTypeQRCode];self.output.rectOfInterest = scanRect;self.preview = [AVCaptureVideoPreviewLayer layerWithSession:self.session];self.preview.videoGravity = AVLayerVideoGravityResizeAspectFill;self.preview.frame = [UIScreen mainScreen].bounds;[self.view.layer insertSublayer:self.preview atIndex:0];//开始捕获[self.session startRunning];}- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{if ( (metadataObjects.count==0) ){return;}if (metadataObjects.count>0) {[self.session stopRunning];AVMetadataMachineReadableCodeObject *metadataObject = metadataObjects.firstObject;//输出扫描字符串UIAlertView *alert = [[UIAlertView alloc] initWithTitle:metadataObject.stringValue message:@"" delegate:self cancelButtonTitle:@"ok" otherButtonTitles: nil];[alert show];}
}- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex
{[self.session startRunning];
}

AVFoundation的部分我就不多介绍了 只要按照上面的代码初始化 即可实现二维码扫描的功能 是不是很简单?

不过这里还不能完全满足我们的要求 因为如果运行以后你会发现 现在是全屏扫描 而不是像我们印象中的那种在屏幕中间有个框 只有在框中的二维码才会被扫描到 不过其实改起来也很简单AVCaptureMetadataOutput有个属性rectOfInterest就是做这个事情的

@interface AVCaptureMetadataOutput : AVCaptureOutput /*!
 @property rectOfInterest
 @abstractSpecifies a rectangle of interest for limiting the search area for visual metadata. @discussionThe value of this property is a CGRect that determines the receiver's rectangle of interest for each frame of video.  The rectangle's origin is top left and is relative to the coordinate space of the device providing the metadata.  Specifying a rectOfInterest may improve detection performance for certain types of metadata. The default value of this property is the value CGRectMake(0, 0, 1, 1).  Metadata objects whose bounds do not intersect with the rectOfInterest will not be returned.*/
@property(nonatomic) CGRect rectOfInterest NS_AVAILABLE_IOS(7_0);@end

可以看到 rectOfInterest的值的范围都是0-1 是按比例取值而不是实际尺寸 不过其实也很简单 只要换算一下就好了 接下来我们添加取景框

CGSize windowSize = [UIScreen mainScreen].bounds.size;CGSize scanSize = CGSizeMake(windowSize.width*3/4, windowSize.width*3/4);
CGRect scanRect = CGRectMake((windowSize.width-scanSize.width)/2, (windowSize.height-scanSize.height)/2, scanSize.width, scanSize.height);//计算rectOfInterest 注意x,y交换位置
scanRect = CGRectMake(scanRect.origin.y/windowSize.height, scanRect.origin.x/windowSize.width, scanRect.size.height/windowSize.height,scanRect.size.width/windowSize.width);
self.output.rectOfInterest = scanRect;self.scanRectView = [UIView new];
[self.view addSubview:self.scanRectView];
self.scanRectView.frame = CGRectMake(0, 0, scanSize.width, scanSize.height);
self.scanRectView.center = CGPointMake(CGRectGetMidX([UIScreen mainScreen].bounds), CGRectGetMidY([UIScreen mainScreen].bounds));
self.scanRectView.layer.borderColor = [UIColor redColor].CGColor;
self.scanRectView.layer.borderWidth = 1;

这里唯一要注意的一点是 rectOfInterest 都是按照横屏来计算的 所以当竖屏的情况下 x轴和y轴要交换一下

看看实际效果

读取


读取主要用到CoreImage 不过要强调的是读取二维码的功能只有在iOS8之后才支持
读取的代码实现就更简单了

UIImage * srcImage = qrcodeImage;CIContext *context = [CIContext contextWithOptions:nil];
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:context options:@{CIDetectorAccuracy:CIDetectorAccuracyHigh}];
CIImage *image = [CIImage imageWithCGImage:srcImage.CGImage];
NSArray *features = [detector featuresInImage:image];
CIQRCodeFeature *feature = [features firstObject];NSString *result = feature.messageString;

看看实际效果

生成


生成也是用到CoreImage 其步骤稍微多一点 代码如下

    NSString *text = self.tfCode.text;NSData *stringData = [text dataUsingEncoding: NSUTF8StringEncoding];//生成CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"];[qrFilter setValue:stringData forKey:@"inputMessage"];[qrFilter setValue:@"M" forKey:@"inputCorrectionLevel"];UIColor *onColor = [UIColor redColor];UIColor *offColor = [UIColor blueColor];//上色CIFilter *colorFilter = [CIFilter filterWithName:@"CIFalseColor"keysAndValues:@"inputImage",qrFilter.outputImage,@"inputColor0",[CIColor colorWithCGColor:onColor.CGColor],@"inputColor1",[CIColor colorWithCGColor:offColor.CGColor],nil];CIImage *qrImage = colorFilter.outputImage;//绘制CGSize size = CGSizeMake(300, 300);CGImageRef cgImage = [[CIContext contextWithOptions:nil] createCGImage:qrImage fromRect:qrImage.extent];UIGraphicsBeginImageContext(size);CGContextRef context = UIGraphicsGetCurrentContext();CGContextSetInterpolationQuality(context, kCGInterpolationNone);CGContextScaleCTM(context, 1.0, -1.0);CGContextDrawImage(context, CGContextGetClipBoundingBox(context), cgImage);UIImage *codeImage = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();CGImageRelease(cgImage);
  • 首先通过[CIFilter filterWithName:@"CIQRCodeGenerator"]生成QRCode
  • 然后通过[CIFilter filterWithName:@"CIFalseColor"]上色(当然这一步不是必须的 如果仅仅需要白底黑块的QRCode 可以跳过这一步)
  • 最后无损放大并绘制QRCode (上面两步生成的QRCode很小 大概是31*31 如果不放大 就会很模糊)

这里要注意的是 在最后一步一定要使用CGContextScaleCTM(context, 1.0, -1.0)来翻转一下图片 不然生成的QRCode就是上下颠倒的哦

看看实际效果

小结


源码和Demo请点这里

如果不需要对QRCode进行深入的探索的话 相信各位看完本文后 绝大多数的关于QRCode的使用问题 应该都可以迎刃而解了 如果需要进一步了解QRCode的原理 我推荐陈皓的这篇二维码的生成细节和原理

文/里脊串(简书作者)
原文链接:http://www.jianshu.com/p/e8f7a257b612
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

IOS 原生二维码、条形码扫描for IOS7 (八)相关推荐

  1. iOS系统原生二维码条形码扫描

    本文讲述如何用系统自带的东东实现二维码扫描的功能:点击当前页面的某个按钮,创建扫描VIEW.细心的小伙伴可以发现 title被改变了,返回按钮被隐藏了.这个代码自己写就行了,与本文关系不大...绿色的 ...

  2. iOS原生二维码扫描(一)

    首先搭建一个最初步的能识别出二维码信息的最基本框架: @interface ScanCodeViewController ()<AVCaptureMetadataOutputObjectsDel ...

  3. iOS 原生二维码扫描和生成

    代码地址如下: http://www.demodashi.com/demo/12551.html 一.效果预览: 功能描述:WSLNativeScanTool是在利用原生API的条件下封装的二维码扫描 ...

  4. iOS 生成二维码/条形码

    级别:★★☆☆☆ 标签:「iOS CIFilter」「CIQRCodeGenerator」「CICode128BarcodeGenerator」「二维码加logo」 作者: Xs·H 审校: QiSh ...

  5. iOS原生二维码扫码实现(含蒙版和扫码动画)

    #一.iOS实现原生扫码的意义 二维码扫码功能对于现在的iOS App开发来说是非常重要的. 通常为了节省开发时间,很多开发者会采用ZXing和ZBar等第三方SDK进行开发. 这样的好处是快速便捷, ...

  6. Android 比Zing 更快的二维码 条形码扫描Zbar

    之前项目要用做二维码 条形码的扫描 找资料 大多都是Zing 的,但是 zing 扫描比较慢 而且经常扫描半天不出来 ,如果是近距离的人话根本不行, 体验很差 ,最主要还可能是对zing 的理解不够透 ...

  7. Android基于Google Zxing实现二维码/条形码扫描、生成二维码/条形码

     二维码/条形码生成器 二维码/条形码扫描器 一.二维码与条形码工作原理 目前的很多应用上都有扫码功能,当时微信推出二维码扫码功能时,觉得imagine,通过一张简单的图片就能扫描添加还有,还有分 ...

  8. Android 基于google Zxing实现二维码 条形码扫描,仿微信二维码扫描效果

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 转载请注 ...

  9. [iOS] 图文讲解原生二维码有效扫描区域 rectOfInterest

    在使用原生的 AVFoundation 框架实现二维码扫描的时候, 需要注意一下两个方面: 启动相机的卡顿问题; 有效扫描区域的问题; 本文主要针对这两个问题进行讲解. 1. 启动扫描卡顿 在Push ...

最新文章

  1. h5 移动端 关于监测切换程序到后台或息屏事件和visibilitychange的使用
  2. inux 下查看服务器负载均衡
  3. xtrabackup全量备份+binlog基于时间点恢复
  4. 99%的产品经理不知道的秘密:如何招程序猿喜欢?
  5. 9、mybatis中动态sql的使用
  6. 自动装箱自动拆箱java,自动装箱?拆箱?==问题?详解java面试常见的一个问题...
  7. 大厂产品是如何做行业调研和规划的?附汇报模版(PPT)
  8. 北漂、杭漂,居大不易,他们是怎么买第一套房子的
  9. 【初始化】神经网络到底该如何初始化?吴恩达Deeplearning.ai最新干货|湾区人工智能...
  10. python免费课程400节-太原Python编程课哪家比较有优势
  11. SlickEdit 之--配置传承
  12. Windows如何对硬盘进行分区?
  13. python获取软件内数据_三种 Python 网络内容抓取工具与爬虫
  14. 关于光伏行业的测试及测试标准
  15. 如何成为一名获得Adobe认证的专业人员?
  16. [20181031]如何确定db_link的进程号.txt
  17. java路由总线_网易考拉Android客户端路由总线设计
  18. 导体接地时的静电平衡问题
  19. Linear Programming线性规划(Introduction to Algorithms, 算法导论,CLRS)学习笔记
  20. 电信校园网 - 断网重连 - 武汉xx学院

热门文章

  1. 犀浦某校一名计算机系大二男生,大学生恋爱事例 和事例分析
  2. 一个html页面上显示dopost,java-无法使HTMLUnit跟随页面上使用__doPostBack...
  3. 华为你学不会思维脑图
  4. 手机office使用 手机office不登录 手机office不联网 手机office查询功能 【手机word】【手机excel】
  5. Springboot使用pagehelper进行分页
  6. 多索引、多类型查询:
  7. Android EditText软键盘换行键的修改和事件监听,软键盘设置搜索按钮;
  8. 百度数据众包,一门AI新基建的“基建”生意
  9. 疫情之下:教育APP软件开发对企业的重要性
  10. POJ 2142 The Balance(扩展欧几里德)