• ios如何给生成的二维码设置样式和logo
  • 发布于 2个月前
  • 179 热度
    0 评论
前言
一个二维码通常会存储字符串内容,所以可以给 String 创建一个扩展,以便捷使用:
extension String {
    func qrCodeImage(size: CGFloat) -> UIImage? {
        guard let filter = CIFilter(name: "CIQRCodeGenerator") else {
            return nil
        }
        // 堆代码 duidaima.com
        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))
    }
}
那么使用的时候就很简单了:
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 的过滤器,传入二维码图像和前景色 + 背景色,最终再从新的过滤器中生成新的图像:
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))
    }
}
然后我们就可以生成一个带有自定义前景色和背景色的二维码了:
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 的方法:
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 参数,贴一下最终代码:
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 了:
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"。

例如:
let qrFilter = CIFilter(name: "CIQRCodeGenerator")
qrFilter?.setValue(data, forKey: "inputMessage") // 必须的参数
qrFilter?.setValue("Q", forKey: "inputCorrectionLevel") // 可选的参数
在这个例子中,我们使用 "Q" 级别的纠错,这意味着即使二维码的 25% 被破坏,也仍然可以被成功读取。这在某些情况下可能会非常有用,例如当二维码可能会被部分遮挡或破坏,或者需要在二维码上添加 logo 时。
用户评论