二维码又称QR Code,QR全称Quick Response,是一个近几年来移动设备上超流行的一种编码方式,它比传统的Bar Code条形码能存更多的信息,也能表示更多的数据类型:比如:字符,数字,日文,中文等等。
在iOS7之后,苹果自身集成了二维码的生成和读取功能。生成二维码包括以下步骤
- 使用CIFilter滤镜类生成二维码
- 对生成的二维码进行加工,使其更清晰
经过上面两步得到会得到原始的二维码图片,在这基础上可以进行以下个性化定制 - 自定义二维码背景色、填充色
- 自定义定位角标
- 在二维码中心插入小图片
二维码生成
本文通过类的方式来生成个性化二维码定制。
初始化二维码信息尺寸和默认值
- (instancetype)init
{
if (self = [super init]) {
//默认值
_info = @"http://mkiltech.com";
_backgroundColor = [UIColor whiteColor];
_fillColor = [UIColor blackColor];
}
return self;
}
- (void)setInfo:(NSString *)info withSize:(CGFloat)size
{
_info = info;
CGFloat scale = [UIScreen mainScreen].scale;
CGRect rect = CGRectMake(0, 0, size, size);
_size = CGRectGetWidth(rect) * scale;
}
使用CIFilter滤镜类生成二维码
- (void)generateQRCodeFilter
{
CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
[filter setDefaults];
NSData *data = [_info dataUsingEncoding:NSUTF8StringEncoding];
[filter setValue:data forKey:@"inputMessage"]; //通过kvo方式给一个字符串,生成二维码
[filter setValue:@"H" forKey:@"inputCorrectionLevel"]; //设置二维码的纠错水平,越高纠错水平越高,可以污损的范围越大
//设置背景颜色和填充颜色 默认白色背景黑色填充
CIColor *color1 = [CIColor colorWithCGColor:_fillColor.CGColor];
CIColor *color2 = [CIColor colorWithCGColor:_backgroundColor.CGColor];
NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys: filter.outputImage ,@"inputImage",
color1,@"inputColor0",
color2,@"inputColor1",nil];
CIFilter *newFilter = [CIFilter filterWithName:@"CIFalseColor" withInputParameters:parameters];
_outPutImage = [newFilter outputImage]; //拿到二维码图片
}
对生成的二维码进行加工,使其更清晰
- (UIImage *)createNonInterpolatedUIImageFormCIImage:(CIImage *)image withSize:(CGFloat) size {
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;
//创建一个DeviceRGB颜色空间
CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
//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)kCGImageAlphaPremultipliedLast);
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];
return outputImage;
}
获取二维码定位图案位置
二维码一共有40个尺寸。官方叫版本Version。Version 1是21 x 21的矩阵,Version 2是 25 x 25的矩阵,Version 3是29的尺寸,每增加一个version,就会增加4的尺寸,公式是:(V-1)4 + 21(V是版本号) 最高Version 40,(40-1)4+21 = 177,所以最高是177 x 177 的正方形。
下面我们看看一个二维码的样例:
QR-Code-Overview.jpeg
定位图案
- Position Detection Pattern是定位图案,用于标记二维码的矩形大小。这三个定位图案有白边叫Separators for Postion Detection Patterns。之所以三个而不是四个意思就是三个就可以标识一个矩形了。
- Timing Patterns也是用于定位的。原因是二维码有40种尺寸,尺寸过大了后需要有根标准线,不然扫描的时候可能会扫歪了。
- Alignment Patterns 只有Version 2以上(包括Version2)的二维码需要这个东东,同样是为了定位用的。
功能性数据
- Format Information 存在于所有的尺寸中,用于存放一些格式化数据的。
- Version Information 在 >= Version 7以上,需要预留两块3 x 6的区域存放一些版本信息。
数据码和纠错码
- 除了上述的那些地方,剩下的地方存放 Data Code 数据码 和 Error Correction Code 纠错码。
获取Version
- (CGFloat)fetchVersion {
return ((_outPutImage.extent.size.width - 21)/4.0 + 1);
}
换算成绘制坐标
定位图案外层坐标
- (UIBezierPath *) outerPositionPathWidth:(CGFloat)width withVersion:(CGFloat )version wihtPosition:(MKQRPosition) position
{
CGFloat zonePathWidth = width/((version - 1) * 4 + 21);
CGFloat positionFrameWidth = zonePathWidth * outerPositionPathOriginLength;
CGPoint topLeftPoint = CGPointMake(zonePathWidth * 1.5, zonePathWidth * 1.5);
CGRect rect = CGRectMake(topLeftPoint.x - 0.2, topLeftPoint.y - 0.2, positionFrameWidth, positionFrameWidth);
rect = CGRectIntegral(rect);
rect = CGRectInset(rect, 1, 1);
UIBezierPath *path;
CGFloat offset;
switch (position) {
case TopLeft:
path = [UIBezierPath bezierPathWithRect:rect];
path.lineWidth = zonePathWidth + 1.5;
path.lineCapStyle = kCGLineCapSquare;
break;
case TopRight:
offset = width - positionFrameWidth - topLeftPoint.x * 2;
rect = CGRectOffset(rect, offset, 0);
path = [UIBezierPath bezierPathWithRect:rect];
path.lineWidth = zonePathWidth + 1.5;
path.lineCapStyle = kCGLineCapSquare;
break;
case BottomLeft:
offset = width - positionFrameWidth - topLeftPoint.x * 2;
rect = CGRectOffset(rect, 0, offset);
path = [UIBezierPath bezierPathWithRect:rect];
path.lineWidth = zonePathWidth + 1.5;
path.lineCapStyle = kCGLineCapSquare;
break;
case QuietZone:
rect = CGRectMake(zonePathWidth * 0.5, zonePathWidth * 0.5, width - zonePathWidth, width - zonePathWidth);
path = [UIBezierPath bezierPathWithRect:rect];
path.lineWidth = zonePathWidth + [UIScreen mainScreen].scale;
path.lineCapStyle = kCGLineCapSquare;
break;
default:
path = [UIBezierPath bezierPath];
break;
}
return path;
}
定位图案内层坐标包括中心图片位置
- (CGRect)innerPositionRectWidth:(CGFloat )width withVersion:(CGFloat )version wihtPosition:(MKQRPosition) position
{
CGFloat leftMargin = width * 3 / ((version - 1) * 4 + 21);
CGFloat tileWidth = leftMargin;
CGFloat centerImageWith = width * 7 / ((version - 1) * 4 + 21);
CGRect rect = CGRectMake(leftMargin + 1.5, leftMargin + 1.5, leftMargin - 3, leftMargin - 3);
rect = CGRectIntegral(rect);
rect = CGRectInset(rect, -1, -1);
CGFloat offset;
switch (position) {
case TopLeft:
break;
case TopRight:
offset = width - tileWidth - leftMargin*2;
rect = CGRectOffset(rect, offset, 0);
break;
case BottomLeft:
offset = width - tileWidth - leftMargin * 2;
rect = CGRectOffset(rect, 0, offset);
break;
case Center:
rect = CGRectMake(CGPointZero.x, CGPointZero.y, centerImageWith, centerImageWith);
offset = width/2 - centerImageWith/2;
rect = CGRectOffset(rect, offset, offset);
break;
default:
rect = CGRectZero;
break;
}
return rect;
}
效果图
QRCodeRendering.png完整代码请前往 https://github.com/ymkil/MKQRCode 如果满意的话,给个星哦!!
转载请注明出处。
网友评论