IOS原生方法实现二维码生成与扫描
转自:http://www.jianshu.com/p/d6663245d3fa
二维码的生成有好多第三方库,如Z-Xing。但是为了控制安装包的大小,或者并不需要其他的一些额外的功能,用系统的方法即可满足.
一、二维码的生成
+ (UIImage *)qrImageForString:(NSString *)string imageSize:(CGFloat)Imagesize logoImageSize:(CGFloat)waterImagesize{
CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
[filter setDefaults];
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
[filter setValue:data forKey:@"inputMessage"];//通过kvo方式给一个字符串,生成二维码
[filter setValue:@"H" forKey:@"inputCorrectionLevel"];//设置二维码的纠错水平,越高纠错水平越高,可以污损的范围越大
CIImage *outPutImage = [filter outputImage];//拿到二维码图片
return [[self alloc] createNonInterpolatedUIImageFormCIImage:outPutImage withSize:Imagesize waterImageSize:waterImagesize];
}
- (UIImage *)createNonInterpolatedUIImageFormCIImage:(CIImage *)image withSize:(CGFloat) size waterImageSize:(CGFloat)waterImagesize{
CGRect extent = CGRectIntegral(image.extent);
CGFloat scale = MIN(size/CGRectGetWidth(extent), size/CGRectGetHeight(extent));
// 1.创建bitmap;
size_t width = CGRectGetWidth(extent) * scale;
size_t height = CGRectGetHeight(extent) * scale;
//创建一个DeviceGray颜色空间
CGColorSpaceRef cs = CGColorSpaceCreateDeviceGray();
//CGBitmapContextCreate(void * _Nullable data, size_t width, size_t height, size_t bitsPerComponent, size_t bytesPerRow, CGColorSpaceRef _Nullable space, uint32_t bitmapInfo)
//width:图片宽度像素
//height:图片高度像素
//bitsPerComponent:每个颜色的比特值,例如在rgba-32模式下为8
//bitmapInfo:指定的位图应该包含一个alpha通道。
CGContextRef bitmapRef = CGBitmapContextCreate(nil, width, height, 8, 0, cs, (CGBitmapInfo)kCGImageAlphaNone);
CIContext *context = [CIContext contextWithOptions:nil];
//创建CoreGraphics image
CGImageRef bitmapImage = [context createCGImage:image fromRect:extent];
CGContextSetInterpolationQuality(bitmapRef, kCGInterpolationNone);
CGContextScaleCTM(bitmapRef, scale, scale);
CGContextDrawImage(bitmapRef, extent, bitmapImage);
// 2.保存bitmap到图片
CGImageRef scaledImage = CGBitmapContextCreateImage(bitmapRef);
CGContextRelease(bitmapRef); CGImageRelease(bitmapImage);
//原图
UIImage *outputImage = [UIImage imageWithCGImage:scaledImage];
//给二维码加 logo 图
UIGraphicsBeginImageContextWithOptions(outputImage.size, NO, [[UIScreen mainScreen] scale]);
[outputImage drawInRect:CGRectMake(0,0 , size, size)];
//logo图
UIImage *waterimage = [UIImage imageNamed:@"icon_imgApp"];
//把logo图画到生成的二维码图片上,注意尺寸不要太大(最大不超过二维码图片的%30),太大会造成扫不出来
[waterimage drawInRect:CGRectMake((size-waterImagesize)/2.0, (size-waterImagesize)/2.0, waterImagesize, waterImagesize)];
UIImage *newPic = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newPic;
}
这个是官方文档中对inputMessage和inputCorrectionLevel的解释
[CIQRCodeGenerator ](https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CoreImageFilterReference/#//apple_ref/doc/filter/ci/CIQRCodeGenerator)
Generates a Quick Response code (two-dimensional barcode) from input data.
Parameters
*inputMessage*
The data to be encoded as a QR code. AnNSData
object whose display name is Message.
*inputCorrectionLevel*
A single letter specifying the error correction format. AnNSString
object whose display name is CorrectionLevel.
Default value:M
Discussion
Generates an output image representing the input data according to the ISO/IEC 18004:2006 standard. The width and height of each module (square dot) of the code in the output image is one point. To create a QR code from a string or URL, convert it to an[NSData](https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSData_Class/index.html#//apple_ref/occ/cl/NSData)object using the[NSISOLatin1StringEncoding](https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/index.html#//apple_ref/c/econst/NSISOLatin1StringEncoding)string encoding.
The*inputCorrectionLevel*
parameter controls the amount of additional data encoded in the output image to provide error correction. Higher levels of error correction result in larger output images but allow larger areas of the code to be damaged or obscured without. There are four possible correction modes (with corresponding error resilience levels):
L : 7%
M : 15%
Q : 25%
H : 30%
Member Of
CICategoryBuiltIn
,CICategoryStillImage
,CICategoryGenerator
Sample Output
**Figure 91**The result of using the CIQRCodeGenerator filter 
修改二维码的颜色,这一段是在网上找的,把生成的二维码图片传入,再传入想要的颜色即可
图片要传没有加过 logo 的
- (UIImage*)imageBlackToTransparent:(UIImage*)image withRed:(CGFloat)red andGreen:(CGFloat)green andBlue:(CGFloat)blue{
const int imageWidth = image.size.width;
const int imageHeight = image.size.height;
size_t bytesPerRow = imageWidth * 4;
uint32_t* rgbImageBuf = (uint32_t*)malloc(bytesPerRow * imageHeight);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(rgbImageBuf, imageWidth, imageHeight, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);
CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), image.CGImage); // 遍历像素
int pixelNum = imageWidth * imageHeight;
uint32_t* pCurPtr = rgbImageBuf;
for (int i = 0; i < pixelNum; i++, pCurPtr++){
if ((*pCurPtr & 0xFFFFFF00) < 0x99999900) // 将白色变成透明
{
// 改成下面的代码,会将图片转成想要的颜色
uint8_t* ptr = (uint8_t*)pCurPtr;
ptr[3] = red; //0~255
ptr[2] = green;
ptr[1] = blue;
} else {
uint8_t* ptr = (uint8_t*)pCurPtr;
ptr[0] = 0;
}
}
// 输出图片
CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, rgbImageBuf, bytesPerRow * imageHeight, nil);
CGImageRef imageRef = CGImageCreate(imageWidth, imageHeight, 8, 32, bytesPerRow, colorSpace, kCGImageAlphaLast | kCGBitmapByteOrder32Little, dataProvider, NULL, true, kCGRenderingIntentDefault);
CGDataProviderRelease(dataProvider);
UIImage* resultUIImage = [UIImage imageWithCGImage:imageRef]; // 清理空间
CGImageRelease(imageRef);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
return resultUIImage;
}
二、扫码
扫码主要用到的是AVFoundation用到的东西和相机基本相同,相机请参考我的另外一篇文章

声明以下对象,遵守AVCaptureMetadataOutputObjectsDelegate
@interface ScanQRViewController ()<AVCaptureMetadataOutputObjectsDelegate>
//捕获设备,通常是前置摄像头,后置摄像头,麦克风(音频输入)
@property(nonatomic)AVCaptureDevice *device;
//AVCaptureDeviceInput 代表输入设备,他使用AVCaptureDevice 来初始化
@property(nonatomic)AVCaptureDeviceInput *input;
//设置输出类型为Metadata,因为这种输出类型中可以设置扫描的类型,譬如二维码
//当启动摄像头开始捕获输入时,如果输入中包含二维码,就会产生输出
@property(nonatomic)AVCaptureMetadataOutput *output;
//session:由他把输入输出结合在一起,并开始启动捕获设备(摄像头)
@property(nonatomic)AVCaptureSession *session;
//图像预览层,实时显示捕获的图像
@property(nonatomic)AVCaptureVideoPreviewLayer *previewLayer;
初始化各对象,输入输出设备结合
- (void)creatCaptureDevice{
//使用AVMediaTypeVideo 指明self.device代表视频,默认使用后置摄像头进行初始化
self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
//使用设备初始化输入
self.input = [[AVCaptureDeviceInput alloc]initWithDevice:self.device error:nil];
//生成输出对象
self.output = [[AVCaptureMetadataOutput alloc]init];
//设置代理,一旦扫描到指定类型的数据,就会通过代理输出
//在扫描的过程中,会分析扫描的内容,分析成功后就会调用代理方法在队列中输出
[self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
//生成会话,用来结合输入输出
self.session = [[AVCaptureSession alloc]init];
if ([self.session canAddInput:self.input]) {
[self.session addInput:self.input];
}
if ([self.session canAddOutput:self.output]) {
[self.session addOutput:self.output];
}
//指定当扫描到二维码的时候,产生输出
//AVMetadataObjectTypeQRCode 指定二维码
//指定识别类型一定要放到添加到session之后
[self.output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];
//设置扫描信息的识别区域,左上角为(0,0),右下角为(1,1),不设的话全屏都可以识别。设置过之后可以缩小信息扫描面积加快识别速度。
//这个属性并不好设置,整了半天也没太搞明白,到底x,y,width,height,怎么是对应的,这是我一点一点试的扫描区域,看不到只能调一下,扫一扫试试
[self.output setRectOfInterest:CGRectMake(0.1 ,0.3 , 0.4, 0.4)];
//使用self.session,初始化预览层,self.session负责驱动input进行信息的采集,layer负责把图像渲染显示
self.previewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:self.session];
self.previewLayer.frame = CGRectMake(0, 0, kScreenWidth , kScreenHeight);
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.view.layer addSublayer:self.previewLayer];
//开始启动
[self.session startRunning];
}
实现代理方法
#pragma mark 输出的代理
//metadataObjects :把识别到的内容放到该数组中
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
//停止扫描
[self.session stopRunning];
[self.timer invalidate];
self.timer = nil;
[self.lineView removeFromSuperview];
if ([metadataObjects count] >= 1) {
//数组中包含的都是AVMetadataMachineReadableCodeObject 类型的对象,该对象中包含解码后的数据
AVMetadataMachineReadableCodeObject *qrObject = [metadataObjects lastObject];
拿到扫描内容在这里进行个性化处理
NSLog(@"识别成功%@",qrObject.stringValue);
}
}
三、遇到的问题和解决办法
(1)二维码上加logo图的时候,图片很模糊,这是由于UIGraphicsBeginImageContextWithOptions里的 scale 造成的,由于 iPhone 的屏幕都是retina屏幕,都是2倍,3倍像素,这里的 scale 要根据屏幕来设置 即[[UIScreen mainScreen] scale]这样图片就会很清晰
(2)setRectOfInterest:设置扫描信息的识别区域,左上角为(0,0),右下角为(1,1),不设的话全屏都可以识别。设置过之后可以缩小信息扫描面积加快识别速度,原来扫描的是整个屏幕的大小,这时候只扫描一块区域,以此加快识别速度。但是这个属性并不好设置,整了半天也没太搞明白,到底x,y,width,height,怎么是对应的,而且是比例不是直接的数字,我是一点一点试的扫描区域,看不到情况,只能调一下,扫一扫试试
最后也没整明白,哪位大神知道,求解答
附上官方解释:
The 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.
原文链接:http://www.jianshu.com/p/d6663245d3fa
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
IOS原生方法实现二维码生成与扫描的更多相关文章
- iOS原生CIFilter创建二维码
iOS原生CIFilter创建二维码 2016-05-31 未来C iOS原生CIFilter创建二维码 关于二维码生成,网上也是有很多,很早以前的第三方库大多数都是通过C++写,也是有的如zxing ...
- 使用IOS7原生API进行二维码条形码的扫描
使用IOS7原生API进行二维码条形码的扫描 IOS7之前,开发者进行扫码编程时,一般会借助第三方库.常用的是ZBarSDK,IOS7之后,系统的AVMetadataObject类中,为我们提供了解析 ...
- Android开发——Android中的二维码生成与扫描
0. 前言 今天这篇文章主要描述二维码的生成与扫描,使用目前流行的Zxing,为什么要讲二维码,因为二维码太普遍了,随便一个Android APP都会有二维码扫描.本篇旨在帮助有需求的同学快速完成二维 ...
- wex5 实战 二维码生成,扫描,蓝牙打印
给人设计了一个小模块,要求是,把一个单号生成二维码,实现扫描查询单号具体信息,并能通过蓝牙把二维码打印出来.功能实现并不复杂,今天一口气把它搞定.来看效果. 一 效果演示: 二.二维码生成 1 在 ...
- 转【微信小程序 四】二维码生成/扫描二维码
原文:https://blog.csdn.net/xbw12138/article/details/75213274 前端 二维码生成 二维码要求:每分钟刷新一次,模拟了个鸡肋,添加了个按分钟显示的时 ...
- Windows phone 8 二维码生成与扫描
1. 二维码的生成 二维码生成用到了一个第三方的插件(zxing.wp8.0) 根据指定的信息,生成对应的二维码. 代码很简单: bool falg=tbk.Text==""?fa ...
- iOS 之 二维码生成与扫描(LBXScan)
参考:https://github.com/MxABC/LBXScan 步骤如下: 1. 下载 通过参考网址进行下载. 2. 导入 导入整个LBXScan文件夹 3. 配置 在pch中加入 #impo ...
- iOS中 扫描二维码/生成二维码详解 韩俊强的博客
最近大家总是问我有没有关于二维码的demo,为了满足大家的需求,特此研究了一番,希望能帮到大家! 每日更新关注:http://weibo.com/hanjunqiang 新浪微博 指示根视图: se ...
- iOS中 扫描二维码/生成二维码具体解释 韩俊强的博客
近期大家总是问我有没有关于二维码的demo,为了满足大家的需求,特此研究了一番,希望能帮到大家! 每日更新关注:http://weibo.com/hanjunqiang 新浪微博 指示根视图: se ...
随机推荐
- Hibernate的多对多关系
1.表的关系: 分别有三个表:课程表.学生表.分数表.课程和学生的关系是多对多的,因为一个学生对应多个课程,而一个课程被多个学生选修.如果用一对多.多对一的观点来看待课程和学生的关系显然是不对的,因为 ...
- 深入理解计算机系统 第三章 程序的机器级表示 part2
这周由于时间和精力有限,只读一小节:3.4.4 压入和弹出栈数据 栈是一种特殊的数据结构,遵循“后进先出”的原则,可以用数组实现,总是从数组的一端插入和删除元素,这一端被称为栈顶. 栈有两个常用指令 ...
- SpringBoot 源码解析 (三)----- Spring Boot 精髓:启动时初始化数据
在我们用 springboot 搭建项目的时候,有时候会碰到在项目启动时初始化一些操作的需求 ,针对这种需求 spring boot为我们提供了以下几种方案供我们选择: ApplicationRunn ...
- C++中对C的扩展学习新增内容———面向对象(继承)函数扩展性及虚函数机制
1.c语言中的多态,动态绑定和静态绑定 void do_speak(void(*speak)()) { speak(); } void pig_speak() { cout << &quo ...
- Netty学习篇⑤--编、解码
前言 学习Netty也有一段时间了,Netty作为一个高性能的异步框架,很多RPC框架也运用到了Netty中的知识,在rpc框架中丰富的数据协议及编解码可以让使用者更加青睐: Netty支持丰富的编解 ...
- java property 配置文件管理工具框架,避免写入 property 乱序
property property 是 java 实现的 property 框架. 特点 优雅地进行属性文件的读取和更新 写入属性文件后属性不乱序 灵活定义编码信息 使用 OO 的方式操作 prope ...
- Django 项目笔记
Django 环境的搭建 Django 安装 pip install django==2.1.4 Django 创建项目 django-admin startproject mysite Django ...
- python高阶函数的使用
目录 python高阶函数的使用 1.map 2.reduce 3.filter 4.sorted 5.小结 python高阶函数的使用 1.map Python内建了map()函数,map()函数接 ...
- Linux菜鸟——常见命令一 权限
Linux对文件和目录的权限位 权限位是十位 第一位 代表文件类型 - 普通文件 d 目录文件 l 链接文件 后面九尾 所有者权限 u = user 所属组权限 g = group 其他人权限 o = ...
- Google Chrome浏览器的编码格式的修改步骤
Chrome Charset插件 大二萌新正在学JavaEE,当学到TomCat时,用到浏览器,但是呢,出现了中文乱码的情况,而又用的是Google,可烦恼了,由于Google Chrome的更新, ...