不得不说,二维码是小日本的一个伟大发明,它密度小、信息容量大、容错能力强、成本低、制作难度低等优点,使得二维码得到广泛的运用,我们可以在二维码里面存储各种信息,如网站链接、移动支付,非常方便,用户只需扫一扫就行,所以我们越来越多的移动应用也将一些信息封装成二维码了。
二维码其实就是由很多0、1组成的数字矩阵,用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息;二维码能够在横向和纵向两个方位同时表达信息,因此能在很小的面积内表达大量的信息。在iOS当中,iOS7以后,系统自身集成了二维码的生成与读取功能,不需要我们再集成第三方了;iOS7以下使用libqrencode库来生成二维码图片。二维码样式大概有以下三种:
1、普通形式的二维码
2、自定义颜色的二维码
3、中间带logo的二维码
下面介绍下二维码生成的详细步骤:
一、普通二维码
我们可以把生成 二维码方法封装到一个工具类或者UIImage类别当中,方便以后调用,先拟好方法名:
+ (UIImage *)createQRImageWithContent:(NSString *)content size:(CGSize)size;
首先,我们需要导入头文件
#import <CoreImage/CoreImage.h>
然后把封装的文字content
用UTF-8
转一下,不转的话可能导致崩溃,再实例化一个CIFilter对象,使用CIFilter滤镜类生成二维码,再通过kvo方式给一个字符串,生成二维码。
NSData *stringData = [content dataUsingEncoding: NSUTF8StringEncoding];
CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
[qrFilter setValue:stringData forKey:@"inputMessage"];//通过kvo方式给一个字符串,生成二维码
二维码都有一定的容错能力,就是有部分污损或者破损都没有关系,照常识别。但是也是有限度的,这根据生成时使用的纠错级别而定,可以有7%~30%左右的损坏,实际上保守一点更好。官方文档显示:
字母代表的容错能力分别为:
L : 7%
M : 15%
Q : 25%
H : 30%
注意:
基本原则:
1、三个角上的“回”及“回”字周围的底色不要动
2、中间部分和不带“回”字的一角是可以填图片的(中间最好)
3、如果中间有小的“回”字,能不变就不变,能少变就少变
4、尽可能放大二维码后再添加图片,不要添加图片后放大
5、生成时尽量选择较高的纠错级别
所以再给qrFilter设置一个容错属性:
[qrFilter setValue:@"H" forKey:@"inputCorrectionLevel"];//设置二维码的纠错水平,越高纠错水平越高,可以污损的范围越大
其实到这里二维码显示就完成了,通过imageWithCGImage
方法转为UIImage用UIImageView显示就可以。但是这样显示会有点模糊,我们需要清晰化处理一下,使二维码更清楚,代码如下:
CIImage *image = qrFilter.outputImage;
CGRect extent = CGRectIntegral(image.extent);
CGFloat scale = MIN(size.width / CGRectGetWidth(extent), size.width / CGRectGetHeight(extent));
size_t width = CGRectGetWidth(extent) * scale;
size_t height = CGRectGetHeight(extent) * scale;
//创建DeviceGray灰度色调空间
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
//创建bitmap
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);
//保存bitmap到图片
CGImageRef scaledImage = CGBitmapContextCreateImage(bitmapRef);
CGContextRelease(bitmapRef);
CGImageRelease(bitmapImage);
CGColorSpaceRelease(colorSpace);
return [UIImage imageWithCGImage: scaledImage];
还有一种绘画方法,也能达到相同效果,代码如下:
//上色
UIColor *onColor = [UIColor blackColor];
UIColor *offColor = [UIColor whiteColor];
CIFilter *colorFilter = [CIFilter filterWithName:@"CIFalseColor"
keysAndValues:
@"inputImage",qrFilter.outputImage,
@"inputColor0",[CIColor colorWithCGColor:onColor.CGColor],
@"inputColor1",[CIColor colorWithCGColor:offColor.CGColor],
nil];
CIImage *qrImage = colorFilter.outputImage;
//绘制
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);
return codeImage;
得到效果图如下:
有一点非常有趣,比如你封装的格式越复杂二维码密度越大,如果你封装的只有数字,比如@"111111",三个"回"字显示的比较大,密度较小,自己体会!显示的效果如下:
二、自定义颜色二维码
由上面显示的二维码可知,一般二维码都是黑白相间的格子组成,但有时我们需要个性化一点的二维码,那就需要定制一个专属颜色,这里所用的方法就是遍历像素, 改变像素点颜色,代码如下:
//改变二维码颜色
+ (UIImage *)createQRImageWithContent:(NSString *)content size:(CGSize)size red:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue{
UIImage *image = [self createQRImageWithContent:content size:size];
int imageWidth = image.size.width;
int imageHeight = image.size.height;
size_t bytesPerRow = imageWidth * 4;
uint32_t *rgbImageBuf = (uint32_t *)malloc(bytesPerRow * imageHeight);
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(rgbImageBuf, imageWidth, imageHeight, 8, bytesPerRow, colorSpaceRef, 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) < 0xd0d0d000) {
//将黑点变成自定义的颜色
uint8_t* ptr = (uint8_t*)pCurPtr;
ptr[3] = red*255;
ptr[2] = green*255;
ptr[1] = blue*255;
}else{
//将白点变成透明色,如不需要变透明则屏蔽
uint8_t* ptr = (uint8_t*)pCurPtr;
ptr[0] = 0;
}
}
//取出图片
CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, rgbImageBuf, bytesPerRow * imageHeight,ProviderReleaseData);
CGImageRef imageRef = CGImageCreate(imageWidth, imageHeight, 8, 32, bytesPerRow, colorSpaceRef,
kCGImageAlphaLast | kCGBitmapByteOrder32Little, dataProvider,
NULL, true, kCGRenderingIntentDefault);
CGDataProviderRelease(dataProvider);
UIImage *resultImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
CGContextRelease(context);
CGColorSpaceRelease(colorSpaceRef);
return resultImage;
}
void ProviderReleaseData(void * info, const void * data, size_t size) {
free((void *)data);
}
效果图如下:
细心的同学发现,用生成普通二维码的时候所用的第二种绘制方法,改变二维码颜色方法更简单,关键代码是这两种颜色:
UIColor *onColor = [UIColor blackColor];//黑点
UIColor *offColor = [UIColor whiteColor];//白点
你只需要把onColor
改为定制颜色就OK了,非常简单!
三、带logo的二维码
我们在外面看到的海报附带的二维码一般都会带个logo,这样又展示了公司的logo又使二维码变的更加漂亮!如果logo不需要切圆角,使用drawInRect
就能实现,代码如下:
+ (UIImage *)createQRImageWithContent:(NSString *)content size:(CGSize)size red:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue withLogo:(UIImage *)logo logoFrame:(CGRect)logoFrame{
UIImage *image = [self createQRImageWithContent:content size:size red:red green:green blue:blue];
//有 logo 则绘制 logo
if (logo != nil) {
UIGraphicsBeginImageContext(image.size);
[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
[logo drawInRect:logoFrame];
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultImage;
}else{
return image;
}
}
效果图如下:
但如果我们需要切圆角而且设计的漂亮点的的话,则需要额外处理,首先处理logo图片,分为五个步骤:
1.先对画布进行裁切
2.填充背景颜色
3.执行绘制logo
4.添加并绘制白色边框
5.白色边框的基础上进行绘制黑色分割线
代码如下:
+ (UIImage *)clipCornerRadius:(UIImage *)image withSize:(CGSize) size{
// 白色border的宽度
CGFloat outerWidth = size.width/15.0;
// 黑色border的宽度
CGFloat innerWidth = outerWidth/10.0;
// 设置圆角
CGFloat corenerRadius = size.width/5.0;
// 为context创建一个区域
CGRect areaRect = CGRectMake(0, 0, size.width, size.height);
UIBezierPath *areaPath = [UIBezierPath bezierPathWithRoundedRect:areaRect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(corenerRadius, corenerRadius)];
// 因为UIBezierpath划线是双向扩展的 初始位置就不会是(0,0)
// origin position
CGFloat outerOrigin = outerWidth/2.0;
CGFloat innerOrigin = innerWidth/2.0 + outerOrigin/1.2;
CGRect outerRect = CGRectInset(areaRect, outerOrigin, outerOrigin);
CGRect innerRect = CGRectInset(outerRect, innerOrigin, innerOrigin);
// 外层path
UIBezierPath *outerPath = [UIBezierPath bezierPathWithRoundedRect:outerRect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(outerRect.size.width/5.0, outerRect.size.width/5.0)];
// 内层path
UIBezierPath *innerPath = [UIBezierPath bezierPathWithRoundedRect:innerRect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(innerRect.size.width/5.0, innerRect.size.width/5.0)];
// 创建上下文
UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);{
// 翻转context
CGContextTranslateCTM(context, 0, size.height);
CGContextScaleCTM(context, 1, -1);
// 1.先对画布进行裁切
CGContextAddPath(context, areaPath.CGPath);
CGContextClip(context);
// 2.填充背景颜色
CGContextAddPath(context, areaPath.CGPath);
UIColor *fillColor = [UIColor colorWithRed:0.85 green:0.85 blue:0.85 alpha:1];
CGContextSetFillColorWithColor(context, fillColor.CGColor);
CGContextFillPath(context);
// 3.执行绘制logo
CGContextDrawImage(context, innerRect, image.CGImage);
// 4.添加并绘制白色边框
CGContextAddPath(context, outerPath.CGPath);
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextSetLineWidth(context, outerWidth);
CGContextStrokePath(context);
// 5.白色边框的基础上进行绘制黑色分割线
CGContextAddPath(context, innerPath.CGPath);
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
CGContextSetLineWidth(context, innerWidth);
CGContextStrokePath(context);
}CGContextRestoreGState(context);
UIImage *radiusImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return radiusImage;
}
经过上面的代码我们实现了白色边框黑色分割线的logo,完成了对logo的处理,当然你想设计自己的形式也是可以的。
处理好了logo,接下来就要把logo绘制到二维码上了,一般头像大小不能大于画布的1/4 。因为这个大小之内的不会遮挡二维码的有效信息,代码如下:
+ (UIImage *)imageWithQRImage:(UIImage *)qrImage logo:(UIImage *)logo logoSize:(CGSize)size{
BOOL opaque = 1.0;
// 获取当前设备的scale
CGFloat scale = [UIScreen mainScreen].scale;
// 创建画布Rect
CGRect bgRect = CGRectMake(0, 0, size.width, size.height);
// 头像大小不能大于画布的1/4 (这个大小之内的不会遮挡二维码的有效信息)
CGFloat logoWidth = (size.width/4);
CGFloat logoHeight = logoWidth;
//调用一个新的切割绘图方法(裁切头像图片为圆角,并添加bored 返回一个newimage)
logo = [self clipCornerRadius:logo withSize:CGSizeMake(logoWidth, logoHeight)];
// 设置头像的位置信息
CGPoint position = CGPointMake(size.width/2, size.height/2);
CGRect logoRect = CGRectMake(position.x-(logoWidth/2), position.y-(logoHeight/2), logoWidth, logoHeight);
// 设置画布信息
UIGraphicsBeginImageContextWithOptions(size, opaque, scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);{// 开启画布
// 翻转context (画布)
CGContextTranslateCTM(context, 0, size.height);
CGContextScaleCTM(context, 1, -1);
// 根据 bgRect 用二维码填充视图
CGContextDrawImage(context, bgRect, qrImage.CGImage);
// 根据newAvatarImage 填充头像区域
CGContextDrawImage(context, logoRect, logo.CGImage);
}CGContextRestoreGState(context);// 提交画布
// 从画布中提取图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 释放画布
UIGraphicsEndImageContext();
return image;
}
最终合成传递多参数的方法:
+ (UIImage *)createQRImageWithContent:(NSString *)content size:(CGSize)size red:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue withLogo:(UIImage *)logo{
UIImage *image = [self createQRImageWithContent:content size:size red:red green:green blue:blue];
//为空则返回
if (!logo) { return image;}
UIImage *resultImage = [self imageWithQRImage:image logo:logo logoSize:logo.size];
return resultImage;
}
该方法包含前面所介绍的三种样式的二维码,需要logo则传logo,需要自定义颜色则传rgb值;效果图如下(明显比对logo不处理时好看):
以上就是二维码显示的一般样式处理!如有其它则特殊处理某部分,万变不离其宗!学会了怎么生成,不如再学习下iOS--二维码的扫描
声明: 转载请注明出处http://www.jianshu.com/p/52abb62d0d39