这里每天分享一个 iOS 的新知识,快来关注我吧
前言
昨天的文章讲了二维码的生成方法,感兴趣可以先去看一下。
一个二维码通常会存储字符串内容,所以可以给 String 创建一个扩展,以便捷使用:
swift
extension String {
func qrCodeImage(size: CGFloat) -> UIImage? {
guard let filter = CIFilter(name: "CIQRCodeGenerator") else {
return nil
}
let data = data(using: .utf8)
filter.setValue(data, forKey: "inputMessage")
guard let ciImage = filter.outputImage else {
return nil
}
let scaleX = size / ciImage.extent.size.width
let scaleY = size / ciImage.extent.size.height
let transform = CGAffineTransform(scaleX: scaleX, y: scaleY)
return UIImage(ciImage: ciImage.transformed(by: transform))
}
}
那么使用的时候就很简单了:
ini
let url = "http://weixin.qq.com/r/QR0qLvXE6D2UKbFNb0jF"
// 创建一个 200 x 200 的二维码
let imageView = UIImageView(image: url.qrCodeImage(size: 200))
view.addSubview(imageView)
今天来讲讲如何自定义二维码的样式。
设置前景色和背景色
要设置前景色和背景色,主要用到 CIFalseColor
这个过滤器,简单原理是使用 CIFilter
创建一个名为 CIFalseColor
的过滤器,传入二维码图像和前景色 + 背景色,最终再从新的过滤器中生成新的图像:
swift
extension String {
/// 生成一个二维码
/// - Parameters:
/// - size: 二维码的尺寸
/// - foregroundColor: 二维码的前景色
/// - backgroundColor: 二维码的背景色
/// - Returns: 最终生成的二维码图片
func qrCodeImage(size: CGFloat,
foregroundColor: UIColor,
backgroundColor: UIColor) -> UIImage? {
guard let filter = CIFilter(name: "CIQRCodeGenerator") else {
return nil
}
let data = data(using: .utf8)
filter.setValue(data, forKey: "inputMessage")
let colorFilter = CIFilter(name: "CIFalseColor", parameters: [
"inputImage": filter.outputImage as Any,
"inputColor0": CIColor(color: foregroundColor),
"inputColor1": CIColor(color: backgroundColor)
])
guard let ciImage = colorFilter?.outputImage else {
return nil
}
let scaleX = size / ciImage.extent.size.width
let scaleY = size / ciImage.extent.size.height
let transform = CGAffineTransform(scaleX: scaleX, y: scaleY)
return UIImage(ciImage: ciImage.transformed(by: transform))
}
}
然后我们就可以生成一个带有自定义前景色和背景色的二维码了:
less
let url = "http://weixin.qq.com/r/QR0qLvXE6D2UKbFNb0jF"
// 创建一个 200 x 200 的二维码
let imageView = UIImageView(image: url.qrCodeImage(size: 200, foregroundColor: .red, backgroundColor: .blue))
view.addSubview(imageView)
设置自定义 logo
我们经常见到二维码的中心会有个自定义 logo,那么如何实现这个功能呢?实际上还是需要在 CIImage
上下功夫。
这次用到的是 CISourceOverCompositing
,这个过滤器可以将两个图层合并,为了使用方便,我们给 CIImage
添加一个 addImage
的方法:
swift
extension CIImage {
/// 向 CIImage 中心添加一个图片
/// - Parameters:
/// - image: 要添加的图片
/// - size: 图片尺寸
/// - Returns: 添加之后的图片
func add(image: UIImage, size: CGFloat) -> CIImage? {
guard let combinedFilter = CIFilter(name: "CISourceOverCompositing") else { return nil }
guard let cgImage = image.cgImage else {
return nil
}
let scaleX = size / image.size.width
let scaleY = size / image.size.height
let scaleTransform = CGAffineTransform(scaleX: scaleX, y: scaleY)
let centerTransform = CGAffineTransform(translationX: extent.midX - (size / 2), y: extent.midY - (size / 2))
combinedFilter.setValue(CIImage(cgImage: cgImage).transformed(by: scaleTransform).transformed(by: centerTransform), forKey: "inputImage")
combinedFilter.setValue(self, forKey: "inputBackgroundImage")
return combinedFilter.outputImage
}
}
最终再给 qrCodeImage
加上 logo
和 logoSize
参数,贴一下最终代码:
swift
extension String {
/// 生成一个二维码
/// - Parameters:
/// - size: 二维码的尺寸
/// - foregroundColor: 二维码的前景色
/// - backgroundColor: 二维码的背景色
/// - logo: 要在二维码中心添加的 logo
/// - logoSize: logo 的尺寸
/// - Returns: 最终生成的二维码图片
func qrCodeImage(size: CGFloat,
foregroundColor: UIColor,
backgroundColor: UIColor,
logo: UIImage,
logoSize: CGFloat) -> UIImage? {
guard let filter = CIFilter(name: "CIQRCodeGenerator") else {
return nil
}
let data = data(using: .utf8)
filter.setValue(data, forKey: "inputMessage")
let colorFilter = CIFilter(name: "CIFalseColor", parameters: [
"inputImage": filter.outputImage as Any,
"inputColor0": CIColor(color: foregroundColor),
"inputColor1": CIColor(color: backgroundColor)
])
guard let ciImage = colorFilter?.outputImage else {
return nil
}
let scaleX = size / ciImage.extent.size.width
let scaleY = size / ciImage.extent.size.height
let transform = CGAffineTransform(scaleX: scaleX, y: scaleY)
let newCIImage = ciImage.transformed(by: transform)
guard let newImage = newCIImage.add(image: logo, size: logoSize) else {
return nil
}
return UIImage(ciImage: newImage)
}
}
这时候就可以使用这个方法往二维码上添加 logo 了:
less
let url = "http://weixin.qq.com/r/QR0qLvXE6D2UKbFNb0jF"
// 创建一个 200 x 200 的二维码
let qrImage = url.qrCodeImage(size: 200,
foregroundColor: .red,
backgroundColor: .blue,
logo: UIImage(named: "logo.png")!,
logoSize: 50)
let imageView = UIImageView(image: qrImage)
view.addSubview(imageView)
最后看下效果:
最后
向二维码添加 logo 之后会盖住一部分二维码,如果 logo 尺寸过大,可能会导致无法正常识别。
如果你在使用过程中遇到这个问题,可以参考下下边的解决方法,二维码生成的时候可以添加一个纠错级别,大概讲一讲:
CIQRCodeGenerator
过滤器支持以下两个参数:
inputMessage
: 一个Data
类型的值,代表要编码的数据。这是必须的参数,因为没有它,二维码就没有任何意义。inputCorrectionLevel
: 一个表示纠错级别的字符串。它可以是 "L", "M", "Q" 或 "H"。"L" 表示 7% 的代码字可以被修正。"M" 表示 15% 的代码字可以被修正。"Q" 表示 25% 的代码字可以被修正。"H" 表示 30% 的代码字可以被修正。这个参数是可选的,如果没有提供,那么将会使用默认级别 "M"。
例如:
less
let qrFilter = CIFilter(name: "CIQRCodeGenerator")
qrFilter?.setValue(data, forKey: "inputMessage") // 必须的参数
qrFilter?.setValue("Q", forKey: "inputCorrectionLevel") // 可选的参数
在这个例子中,我们使用 "Q" 级别的纠错,这意味着即使二维码的 25% 被破坏,也仍然可以被成功读取。这在某些情况下可能会非常有用,例如当二维码可能会被部分遮挡或破坏,或者需要在二维码上添加 logo 时。
这里每天分享一个 iOS 的新知识,快来关注我吧
本文同步自微信公众号 "iOS新知",每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!