@xifenglang-33250
2016-10-19T23:56:14.000000Z
字数 6362
阅读 1607
《Swift3.0朝圣之路》
Xcode8 / Swift3.0 / iOS8.0+
二维码在生活中应用非常广泛,在开发中也经常遇到,以前多数使用第三方库/框架生成或识别二维码,今天将介绍使用Swift(OC)原生框架类进行二维码图片的识别和生成,顺便造个Swift3.0版的轮子:
- 识别图片中的二维码
- 生成二维码图片
- 用CIImage重绘UIImage
- 生成RGB彩色二维码图片
- 在二维码上添加Logo
识别二维码图片将使用CIDetector
类,CIDetector
类还支持人脸识别,即使用CIDetectorTypeFace
类型,识别二维码则使用CIDetectorTypeQRCode
。
/// 识别图片二维码
class func jk_recognizeQRCodeImage(_ image: UIImage?, completionHandle: @escaping((String?, NSError?) -> Void)) -> Void {
/// CIDetector可用于人脸识别 CIDetectorTypeFace CIDetectorTypeText
let detector = CIDetector.init(ofType: CIDetectorTypeQRCode, context: nil, options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])
/// 获取的特征组
let features = detector?.features(in: CIImage.init(cgImage: (image?.cgImage)!))
if features != nil && (features?.count)! > 0 {
/// 读取结果
let feature:CIQRCodeFeature = features?.first as! CIQRCodeFeature
completionHandle(feature.messageString, nil)
}
}
CIFilter是一个滤镜类,主要用于图片处理,也可用于生成二维码图片,CIFilter类的基本使用方法可以参考这篇文章《iOS 中的CIFilter(基础用法)》。CIFilter导出CIImage对象,可在此基础上绘制自定义的图片,更改大小、颜色等。
/// 生成黑白二维码ciImage
private class func jk_QRCodeImage(withString content: String?, completionHandle: @escaping((CIImage?, NSError?) -> Void)) -> Void{
let data = content?.data(using: .utf8)
/// 创建二维码滤镜
let filter = CIFilter.init(name: "CIQRCodeGenerator")
filter?.setDefaults()
/// 数据源
filter?.setValue(data, forKey: "inputMessage")
/// L M Q H 修正等级,应该跟采样有关
filter?.setValue("H", forKey: "inputCorrectionLevel")
/// 黑白色的图片
let ciImage = filter?.outputImage
completionHandle(ciImage,nil)
}
绘图的方法除了Swift3.0格式外,较网上流传的代码还做了点更改,这里传入目标大小CGSize,而不是传缩放因子scale,直接传sclae不容易理解,传CGSize更直观,内部通过Size计算contextRef?.scaleBy(x: scale, y: scale)
用到的scale即可。contextRef?.scaleBy即OC中的CGContextScaleCTM
,可查看此文章了解《CGContextTranslateCTM,CGContextScaleCTM,CGContextRotateCTM详解》
- Swift3.0中传入多个枚举值的方法,使用
.rawValue | .rawValue
CGImageAlphaInfo.noneSkipFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue
/// 用CIImage重绘UIImage
class func jk_resize(ciImage: CIImage, size: CGSize, completionHandle: @escaping((UIImage?, NSError?) -> Void)) -> Void {
let width = size.width * UIScreen.main.scale
let height = size.height * UIScreen.main.scale
/// 计算合适的缩放比例
let scale = min(width, height) / min(ciImage.extent.width, ciImage.extent.height)
/// 适合生成RGB色值的彩色图片(兼容黑白色)
let colorSpaceRef = CGColorSpaceCreateDeviceRGB()
var contextRef = CGContext.init(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpaceRef, bitmapInfo:CGImageAlphaInfo.noneSkipFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue)
/// 如果不需要更改二维码的颜色,即黑白色,用下面的代码即可
// let colorSpaceRef = CGColorSpaceCreateDeviceGray()
// var contextRef = CGContext.init(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpaceRef, bitmapInfo:CGImageAlphaInfo.none.rawValue)
let context = CIContext.init(options: nil)
var imageRef = context.createCGImage(ciImage, from: ciImage.extent)
contextRef?.interpolationQuality = CGInterpolationQuality.init(rawValue: CGInterpolationQuality.none.rawValue)!
contextRef?.scaleBy(x: scale, y: scale)
contextRef?.draw(imageRef!, in: ciImage.extent)
let newImage = contextRef?.makeImage()
/// Swift3.0应该对CG/CF开头的类/结构体做了ARC适配,已经没了Release方法。
contextRef = nil
imageRef = nil
completionHandle(UIImage.init(cgImage: newImage!),nil)
}
生成黑白色的二维码图片
/// 生成二维码图片
class func jk_QRCodeImage(withString content: String?, size: CGSize, completionHandle: @escaping((UIImage?, NSError?) -> Void)) -> Void{
self.jk_QRCodeImage(withString: content) { (ciImage, error) in
if (error != nil){
completionHandle(nil, error)
}else {
self.jk_resize(ciImage: ciImage!, size: size, completionHandle: completionHandle)
}
}
}
生成RGB色值二维码图片
主要是在通过ciImage创建新的颜色滤镜CIFilter.init(name: "CIFalseColor")
,通过KVC替换普通二维码中的黑色和白色。colorFilter?.setValue(rgbColor, forKey: "inputColor0")
,inputColor0可用替换黑色,inputColor1可用于替换白底色。
/// 生成彩色的二维码
class func jk_QRCodeImage(withString content: String?, rgbColor: CIColor, size: CGSize, completionHandle: @escaping((UIImage?, NSError?) -> Void)) -> Void{
self.jk_QRCodeImage(withString: content) { (ciImage, error) in
if (error != nil) {
completionHandle(nil, error)
}else {
/// 创建颜色滤镜
let colorFilter = CIFilter.init(name: "CIFalseColor")
colorFilter?.setDefaults()
colorFilter?.setValue(ciImage, forKey: "inputImage")
/// 替换黑色
colorFilter?.setValue(rgbColor, forKey: "inputColor0")
/// 默认白色,可自行替换
colorFilter?.setValue(CIColor.init(red: 1, green: 1, blue: 1), forKey: "inputColor1")
let codeImage = colorFilter?.outputImage
self.jk_resize(ciImage: codeImage!, size: size, completionHandle: completionHandle)
}
}
突然想起我面试中的机试便是合成2张多线程队列下载的图片,跟下面的代码相似。
- Logo的宽度取二维码图片的1/4宽度比较合适,位置放中间。如果对二维码的规则好奇,可戳二维码生成细节和原理
/// 添加LOGO小图
class func jk_addLogo(logo: UIImage?, forQRCodeImage QRImage: UIImage?, completionHandle: @escaping((UIImage?, NSError?) -> Void)) -> Void {
let qrImageBounds = CGRect.init(x: 0, y: 0,
width: (QRImage?.size.width)!,
height: (QRImage?.size.height)!)
let logoSize = CGSize.init(width: qrImageBounds.size.width * 0.25,
height: qrImageBounds.size.height * 0.25)
let x = (qrImageBounds.width - logoSize.width) * 0.5
let y = (qrImageBounds.height - logoSize.height) * 0.5
UIGraphicsBeginImageContext(qrImageBounds.size)
QRImage?.draw(in: qrImageBounds)
logo?.draw(in: CGRect.init(x: x, y: y,
width: logoSize.width,
height: logoSize.height))
let resultImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
completionHandle(resultImage,nil)
}
}
栗子
/// 识别二维码图片
JKQRCodeTool.jk_recognizeQRCodeImage(UIImage.init(named: "JKSwiftGitHub"), completionHandle: { (str, error) in
if error != nil {
JKLOG(error)
} else {
JKLOG(str)
/// 生成二维码图片
/// 黑白色
/*
JKQRCodeTool.jk_QRCodeImage(withString: str, size: CGSize.init(width: 500, height: 500), completionHandle: { (image, error1) in
if error1 != nil {
JKLOG(error1)
} else {
imageView.image = image
}
})
*/
/// 彩色
JKQRCodeTool.jk_QRCodeImage(withString: str, rgbColor: CIColor.init(red: 1, green: 0, blue: 0, alpha: 1), size: CGSize.init(width: 200, height: 200), completionHandle: { (image, error2) in
if error2 != nil {
JKLOG(error2)
} else {
self.imageView?.image = image
/// 添加LOGO
JKQRCodeTool.jk_addLogo(logo: UIImage.init(named: "logo"), forQRCodeImage: image, completionHandle: { (qrImage, error3) in
if error3 != nil {
JKLOG(error3)
} else {
self.imageView?.image = qrImage
/// 识别二维码图片
JKQRCodeTool.jk_recognizeQRCodeImage(qrImage, completionHandle: { (str, error4) in
if error4 != nil {
JKLOG(error4)
} else {
JKLOG(str)
}
})
}
})
}
})
}
我所有Swift3.0练习Demo都放到了Github上,并且在不断更新。
Swift3.0朝圣之路-全集地址