iOS开发-定制多样式二维码
iOS开发-定制多样式二维码
二维码/条形码是按照某种特定的几何图形按一定规律在平台(一维/二维方向上)分布的黑白相间的图形纪录符号信息。使用若干个与二进制对应的几何形体来表示文字数值信息。

最常见的二维码功能包括信息获取、网站跳转、电商交易、手机支付等等,其拥有密度小、信息容量大、容错能力强、成本低、制作难度低等优点。在移动开发中,二维码的地位也越来越重要,掌握二维码的基本操作是重要的本领之一。
在iOS7之后,苹果自身集成了二维码的生成和读取功能。生成二维码包括以下步骤
1、导入CoreImage/CoreImage.h头文件
2、使用CIFilter滤镜类生成二维码
3、对生成的二维码进行加工,使其更清晰
除了上述三个步骤之外,我们还可以对二维码进行进一步的拓展处理
1、自定义二维码图案颜色
2、在二维码中心插入圆角小图片
3、在圆角图片下面加上一层圆角白色图片
二维码生成
码农们生产代码的同时永远不要忘记尽可能的复用,那么为了实现这种目的,本文的代码通过类别拓展UIImage的方法来完成。我们先声明并实现一个类方法用来接收二维码存储数据以及二维码尺寸的方法:
+ (UIImage *)imageOfQRFromURL: (NSString *)networkAddress codeSize: (CGFloat)codeSize {
if (!networkAddress|| (NSNull *)networkAddress == [NSNull null]) { return nil; }
codeSize = [self validateCodeSize: codeSize];
CIImage * originImage = [self createQRFromAddress: networkAddress];
UIImage * result = [UIImage imageWithCIImage: originImage];
return result;
}
在上面的代码里面,我们总共做了四件事情:验证存储信息的有效性;验证二维码尺寸的合理大小;使用存储信息生成二维码;将二维码转成UIImage返回。这些方法的实现分别如下:
/*! 验证二维码尺寸合法性*/
+ (CGFloat)validateCodeSize: (CGFloat)codeSize
{
codeSize = MAX(160, codeSize);
codeSize = MIN(CGRectGetWidth([UIScreen mainScreen].bounds) - 80, codeSize);
return codeSize;
}
/*! 利用系统滤镜生成二维码图*/
+ (CIImage *)createQRFromAddress: (NSString *)networkAddress
{
NSData * stringData = [networkAddress dataUsingEncoding: NSUTF8StringEncoding];
CIFilter * qrFilter = [CIFilter filterWithName: @"CIQRCodeGenerator"];
[qrFilter setValue: stringData forKey: @"inputMessage"];
[qrFilter setValue: @"H" forKey: @"inputCorrectionLevel"];
return qrFilter.outputImage;
}
ps:对于CIFilter想要更进一步了解,可以在xcode中使用快捷键shift+command+0打开文档,然后搜索core image filter reference获取更多滤镜的使用方法,这些滤镜可以用来实现类似美图秀秀的修图功能。
上面的代码生成了一个粗略的二维码图,我们需要对图片再进行一次处理,使其清晰化。因为,我们需要另外一个类别方法:
/*! 对图像进行清晰化处理*/
+ (UIImage *)excludeFuzzyImageFromCIImage: (CIImage *)image size: (CGFloat)size
{
CGRect extent = CGRectIntegral(image.extent);
CGFloat scale = MIN(size / CGRectGetWidth(extent), size / CGRectGetHeight(extent));
size_t width = CGRectGetWidth(extent) * scale;
size_t height = CGRectGetHeight(extent) * scale;
//创建灰度色调空间
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
CGContextRef bitmapRef = CGBitmapContextCreate(nil, width, height, 8, 0, colorSpace, (CGBitmapInfo)kCGImageAlphaNone);
CIContext * context = [CIContext contextWithOptions: nil];
CGImageRef bitmapImage = [context createCGImage: image fromRect: extent];
CGContextSetInterpolationQuality(bitmapRef, kCGInterpolationNone);
CGContextScaleCTM(bitmapRef, scale, scale);
CGContextDrawImage(bitmapRef, extent, bitmapImage);
CGImageRef scaledImage = CGBitmapContextCreateImage(bitmapRef);
CGContextRelease(bitmapRef);
CGImageRelease(bitmapImage);
CGColorSpaceRelease(colorSpace);
return [UIImage imageWithCGImage: scaledImage];
}
那么这时候,我们把+(UIImage *)imageOfQRFromURL: codeSize: 的最后改成
UIImage * result =[self excludeFuzzyImageFromCIImage: originImage size: codeSize];
示例完成后生成的二维码效果图如下:

二维码拓展
单一的黑白色二维码并不一定总能满足开发的需求或者说领导的需求。好比现在的应用很多功能界面上都在朝着微信学习,这就包括了更多色彩,更多样式的二维码。本文将从颜色、二维码中心小图案这两点入手讲解如何制作类似微信生成我的二维码的样式。
自定义二维码颜色的实现思路是,遍历生成的二维码的像素点,将其中为白色的像素点填充为透明色,非白色则填充为我们自定义的颜色。但是,这里的白色并不单单指纯白色,rgb值高于一定数值的灰色我们也可以视作白色处理。在这里我对白色的定义为rgb值高于0xd0d0d0的颜色值为白色,这个值并不是确定的,大家可以自己设置。基于颜色的设置,我们将原有生成二维码的方法接口改成这样:
+ (UIImage *)imageOfQRFromURL: (NSString *)networkAddress codeSize: (CGFloat)codeSize red: (NSUInteger)red green: (NSUInteger)green blue: (NSUInteger)blue {
if (!networkAddress || (NSNull *)networkAddress == [NSNull null]) { return nil; }
/** 颜色不可以太接近白色*/
NSUInteger rgb = (red << 16) + (green << 8) + blue;
NSAssert((rgb & 0xffffff00) <= 0xd0d0d000, @"The color of QR code is two close to white color than it will diffculty to scan");
codeSize = [self validateCodeSize: codeSize];
CIImage * originImage = [self createQRFromAddress: networkAddress];
UIImage * progressImage = [self excludeFuzzyImageFromCIImage: originImage size: codeSize]; //到了这里二维码已经可以进行扫描了
UIImage * effectiveImage = [self imageFillBlackColorAndTransparent: progressImage red: red green: green blue: blue]; //进行颜色渲染后的二维码
return effectiveImage;
}
相较于前面的代码,多了两个步骤:判断rgb的有效值;对二维码进行颜色渲染。颜色渲染的过程包括获取图像的位图上下文、像素替换、二进制图像转换等操作,具体代码如下:
/*! 对生成二维码图像进行颜色填充*/
+ (UIImage *)imageFillBlackColorAndTransparent: (UIImage *)image red: (NSUInteger)red green: (NSUInteger)green blue: (NSUInteger)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, (CGRect){(CGPointZero), (image.size)}, image.CGImage);
//遍历像素
int pixelNumber = imageHeight * imageWidth;
[self fillWhiteToTransparentOnPixel: rgbImageBuf pixelNum: pixelNumber red: red green: green blue: blue];
CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, rgbImageBuf, bytesPerRow, ProviderReleaseData);
CGImageRef imageRef = CGImageCreate(imageWidth, imageHeight, 8, 32, bytesPerRow, colorSpace, kCGImageAlphaLast | kCGBitmapByteOrder32Little, dataProvider, NULL, true, kCGRenderingIntentDefault);
UIImage * resultImage = [UIImage imageWithCGImage: imageRef];
CGImageRelease(imageRef);
CGColorSpaceRelease(colorSpace);
CGContextRelease(context);
return resultImage;
}
/*! 遍历所有像素点进行颜色替换*/
+ (void)fillWhiteToTransparentOnPixel: (uint32_t *)rgbImageBuf pixelNum: (int)pixelNum red: (NSUInteger)red green: (NSUInteger)green blue: (NSUInteger)blue {
uint32_t * pCurPtr = rgbImageBuf;
for (int i = 0; i < pixelNum; i++, pCurPtr++) {
if ((*pCurPtr & 0xffffff00) < 0xd0d0d000) {
uint8_t * ptr = (uint8_t *)pCurPtr;
ptr[3] = red;
ptr[2] = green;
ptr[1] = blue;
} else {
//将白色变成透明色
uint8_t * ptr = (uint8_t *)pCurPtr;
ptr[0] = 0;
}
}
}
void ProviderReleaseData(void * info, const void * data, size_t size) {
free((void *)data);
}
ps:在修改代码之前,应该想清楚是否需要删除原有代码。类似这种二维码的扩展,旧的二维码生成接口可以留下来,然后在其中调用多参数的全能构造器(Designated Initializer)。

这时候距离微信还差一小步,我们要在二维码的中心位置插入我们的小头像,最直接的方式是加载完我们的头像后,直接drawInRect:。这种实现方法是正确的,但是在我们画上去之前,我们还需要对图像进行圆角处理。(省事的可能直接用imageView加载头像,然后设置头像的cornerRadius,这个也能实现效果)。
到了这个时候,我们需要一个更多参数的二维码生成方法接口了,这次新增的参数应该包括插入图片、圆角半径这些参数,因此方法如下:
+ (UIImage *)imageOfQRFromURL: (NSString *)networkAddress codeSize: (CGFloat)codeSize red: (NSUInteger)red green: (NSUInteger)green blue: (NSUInteger)blue insertImage: (UIImage *)insertImage roundRadius: (CGFloat)roundRadius {
if (!networkAddress || (NSNull *)networkAddress == [NSNull null]) { return nil; }
/** 颜色不可以太接近白色*/
NSUInteger rgb = (red << 16) + (green << 8) + blue;
NSAssert((rgb & 0xffffff00) <= 0xd0d0d000, @"The color of QR code is two close to white color than it will diffculty to scan");
codeSize = [self validateCodeSize: codeSize];
CIImage * originImage = [self createQRFromAddress: networkAddress];
UIImage * progressImage = [self excludeFuzzyImageFromCIImage: originImage size: codeSize]; //到了这里二维码已经可以进行扫描了
UIImage * effectiveImage = [self imageFillBlackColorAndTransparent: progressImage red: red green: green blue: blue]; //进行颜色渲染后的二维码
return [self imageInsertedImage: effectiveImage insertImage: insertImage radius: roundRadius];
}
这次的生成方法同样也只需要进行一次额外的调用方法操作,在插入图片的时候我们需要注意,类似微信的图中图二维码中间的小头像是有一个圆角的白色边缘的,这个边缘的加入让头像显示的更加自然。那么要完成这个效果,我额外在项目中加入了一张白色背景的小图,同样对这张图片进行圆角化处理,然后加在头像的下面。作为头像下方的白色背景图像尺寸应该大于头像图。制作画中画效果的具体实现如下:
/*! 在二维码原图中心位置插入圆角图像*/
+ (UIImage *)imageInsertedImage: (UIImage *)originImage insertImage: (UIImage *)insertImage radius: (CGFloat)radius {
if (!insertImage) { return originImage; }
insertImage = [UIImage imageOfRoundRectWithImage: insertImage size: insertImage.size radius: radius];
UIImage * whiteBG = [UIImage imageNamed: @"whiteBG"];
whiteBG = [UIImage imageOfRoundRectWithImage: whiteBG size: whiteBG.size radius: radius];
//白色边缘宽度
const CGFloat whiteSize = 2.f;
CGSize brinkSize = CGSizeMake(originImage.size.width / 4, originImage.size.height / 4);
CGFloat brinkX = (originImage.size.width - brinkSize.width) * 0.5;
CGFloat brinkY = (originImage.size.height - brinkSize.height) * 0.5;
CGSize imageSize = CGSizeMake(brinkSize.width - 2 * whiteSize, brinkSize.height - 2 * whiteSize);
CGFloat imageX = brinkX + whiteSize;
CGFloat imageY = brinkY + whiteSize;
UIGraphicsBeginImageContext(originImage.size);
[originImage drawInRect: (CGRect){ 0, 0, (originImage.size) }];
[whiteBG drawInRect: (CGRect){ brinkX, brinkY, (brinkSize) }];
[insertImage drawInRect: (CGRect){ imageX, imageY, (imageSize) }];
UIImage * resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultImage;
}
+ (UIImage *)imageOfRoundRectWithImage: (UIImage *)image size: (CGSize)size radius: (CGFloat)radius
{
if (!image) { return nil; }
const CGFloat width = size.width;
const CGFloat height = size.height;
radius = MAX(5.f, radius);
radius = MIN(10.f, radius);
UIImage * img = image;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedFirst);
CGRect rect = CGRectMake(0, 0, width, height);
//绘制圆角
CGContextBeginPath(context);
addRoundRectToPath(context, rect, radius, img.CGImage);
CGImageRef imageMasked = CGBitmapContextCreateImage(context);
img = [UIImage imageWithCGImage: imageMasked];
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
CGImageRelease(imageMasked);
return img;
}
ps:图像绘制圆角是通过在图像上下文中画出圆角矩形的路径,然后进行裁剪,这样就能实现图片的圆角化。

在代码中,对中心位置的头像限制尺寸为二维码的四分之一,这个尺寸下的头像不失清晰度,而且图片尺寸也不至于遮盖了二维码的存储数据。上面的方法都可以在头文件中开发方法接口使用,这将实现这些代码的复用。另外,所有本文中写到的生成二维码的接口都应该在头文件中声明,并且在其实现中调用全能方法(不应当仅仅是构造器需要遵循Designated Initializer的原则):
+ (UIImage *)imageOfQRFromURL: (NSString *)networkAddress {
return [self imageOfQRFromURL: networkAddress codeSize: 100.0f red: 0 green: 0 blue: 0 insertImage: nil roundRadius: 0.f];
}
iOS开发-定制多样式二维码的更多相关文章
- iOS - 定制多样式二维码
二维码/条形码是按照某种特定的几何图形按一定规律在平台(一维/二维方向上)分布的黑白相间的图形纪录符号信息.使用若干个与二进制对应的几何形体来表示文字数值信息. 最常见的二维码功能包括信息获取.网 ...
- iOS开发——高级篇——二维码的生产和读取
一.二维码的生成 从iOS7开始集成了二维码的生成和读取功能此前被广泛使用的zbarsdk目前不支持64位处理器 生成二维码的步骤:导入CoreImage框架通过滤镜CIFilter生成二维码 二维码 ...
- iOS开发之生成二维码
一.二维码的生成 从iOS7开始集成了二维码的生成和读取功能 此前被广泛使用的zbarsdk目前不支持64位处理器 1.二维码的内容(传统的条形码只能放数字) 纯文本 名片 URL 2.生成二 ...
- iOS开发——高级技术&二维码功能的实现
二维码功能的实现 ZBarSDK,一个比较优秀的开源项目,使用起来也很简单. ZBarSDK是一个开源的SDK,可从这里下载到源码,该SDK实现了识别和读取各种条形码,包括EAN-13/UPC-A, ...
- Android开发——通过扫描二维码,打开或者下载Android应用
Android开发——通过扫描二维码,打开或者下载Android应用 在实现这个功能的时候,被不同的浏览器折磨的胃疼,最后实现了勉强能用,也查考了一下其他人的博客 android实现通过浏览器点击 ...
- iOS使用Zbar扫描二维码
iOS使用Zbar扫描二维码 标签(空格分隔):二维码扫描 iOS Zbar64位 正文: 首先下载一个支持64位系统的ZbarSDK的包,保存在了我的云盘里,地址:ZbarSDK 把文件拖到工程里面 ...
- iOS原生CIFilter创建二维码
iOS原生CIFilter创建二维码 2016-05-31 未来C iOS原生CIFilter创建二维码 关于二维码生成,网上也是有很多,很早以前的第三方库大多数都是通过C++写,也是有的如zxing ...
- 分享:Java 开发精美艺术二维码
博客地址:https://ainyi.com/58 Java 开发精美艺术二维码 看到网络上各种各样的二维码层出不穷,好像很炫酷的样子,一时兴起,我也要制作这种炫酷二维码效果 例如: 根据以往例子 根 ...
- iOS 根据url生成二维码贴到底图上
根据url 生成指定尺寸的二维码图片 UIImage * createBinaryCodeImg(const char * url ,CGFloat size) { //create binary c ...
随机推荐
- MySQL模糊查询:LIKE模式和REGEXP模式
MySQL模糊查询提供了两种模式:LIKE模式和REGEXP模式. LIKE模式 LIKE模式是使用的LIKE 或 NOT LIKE 比较运算符进行模糊查询. SELECT 字段 FROM 表 WHE ...
- 【SQL Server】数据库是单个用户的 无法顺利进行操作 怎么解决
1.打开数据库 2.新建查询 ,输入以下的SQL 语句 DECLARE @SQL VARCHAR(MAX); SET @SQL='' SELECT @SQL=@SQL+'; KILL '+RTRIM( ...
- 【前台 ajax】web项目前台传递数组给后台 两种方式
项目使用maven springMVC 有需求 将前台的数组 在ajax中 送给后台 方式1: 前台代码:[注意:ajax中的属性---traditional:true, ] 如果Post ...
- hive脚本出现Error: java.lang.RuntimeException: Error in configuring object和Caused by: java.lang.IndexOutOfBoundsException: Index: 9, Size: 9
是在reduce阶段报的错误,详细错误信息是 朱传豪 19:04:48 Diagnostic Messages for this Task: Error: java.lang.RuntimeExcep ...
- 【转】Spark性能优化指南——基础篇
http://mp.weixin.qq.com/s?__biz=MjM5NDMwNjMzNA==&mid=2651805828&idx=1&sn=2f413828d1fdc6a ...
- Centos系统上安装VNC view
一.安装 VNC 默认情况下,CentOS 6.4 是没有安装的,检查是否安装,输入: [root@localhost ~]# rpm -q vnc vnc-server package vnc is ...
- SpringRMI解析3-RmiServiceExporter逻辑细节
在发布RMI服务的流程中,有几个步骤可能是我们比较关心的. 获取registry 由于底层的封装,获取Registry实例是非常简单的,只需要使用一个函数LocateRegistry.createRe ...
- POJ 3415 后缀数组
题目链接:http://poj.org/problem?id=3415 题意:给定2个串[A串和B串],求两个串公共子串长度大于等于k的个数. 思路:首先是两个字符串的问题.所以想用一个'#'把两个字 ...
- PHP public private protected 三种修饰符的区别
public 表示全局,类内部外部子类都可以访问:private表示私有的,只有本类内部可以使用:protected表示受保护的,只有本类或子类或父类中可以访问:
- Android手机自动化测试真机运行
一, 打开手机的USB调试模式 不同的手机有不同的方法打开usb调试模式,可是去网上查一下你手机的调试模式打开办法(http://wenku.baidu.com/view/3077f06c25c ...