iOS--各种样式的二维码生成

不得不说,二维码是小日本的一个伟大发明,它密度小、信息容量大、容错能力强、成本低、制作难度低等优点,使得二维码得到广泛的运用,我们可以在二维码里面存储各种信息,如网站链接、移动支付,非常方便,用户只需扫一扫就行,所以我们越来越多的移动应用也将一些信息封装成二维码了。

二维码

二维码其实就是由很多0、1组成的数字矩阵,用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息;二维码能够在横向和纵向两个方位同时表达信息,因此能在很小的面积内表达大量的信息。在iOS当中,iOS7以后,系统自身集成了二维码的生成与读取功能,不需要我们再集成第三方了;iOS7以下使用libqrencode库来生成二维码图片。二维码样式大概有以下三种:

1、普通形式的二维码
2、自定义颜色的二维码
3、中间带logo的二维码

下面介绍下二维码生成的详细步骤:

一、普通二维码

我们可以把生成 二维码方法封装到一个工具类或者UIImage类别当中,方便以后调用,先拟好方法名:

+ (UIImage *)createQRImageWithContent:(NSString *)content size:(CGSize)size;

首先,我们需要导入头文件

#import <CoreImage/CoreImage.h>

然后把封装的文字contentUTF-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不切圆角的二维码

  但如果我们需要切圆角而且设计的漂亮点的的话,则需要额外处理,首先处理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不处理时好看):

带logo的二维码

  以上就是二维码显示的一般样式处理!如有其它则特殊处理某部分,万变不离其宗!学会了怎么生成,不如再学习下iOS--二维码的扫描

声明: 转载请注明出处http://www.jianshu.com/p/52abb62d0d39

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容