我有一些扩展方法,用于将二维码转换为图像。在 iOS 17 更新之前,一切都运行得非常完美。它们现在根本没有渲染。
这些是我正在使用的方法
extension UIImage {
convenience init?(qrcode string: String) {
if let outputCIImage = CIImage.make(qrcode: string) {
let context = CIContext()
guard let cgImage = context.createCGImage(outputCIImage, from: outputCIImage.extent) else { return nil }
self.init(cgImage: cgImage)
return
}
return nil
}
convenience init?(qrcode string: String, using color: UIColor) {
if let outputCIImage = CIImage.make(qrcode: string)?.tinted(using: color) {
let context = CIContext()
guard let cgImage = context.createCGImage(outputCIImage, from: outputCIImage.extent) else { return nil }
self.init(cgImage: cgImage)
return
}
return nil
}
}
extension CIImage {
static func make(qrcode string: String) -> CIImage? {
let data = string.data(using: .ascii)
if let filter = CIFilter(name: "CIQRCodeGenerator") {
filter.setValue(data, forKey: "inputMessage")
filter.setValue("Q", forKey: "inputCorrectionLevel")
let transform = CGAffineTransform(scaleX: 12, y: 12)
return filter.outputImage?.transformed(by: transform)
}
return nil
}
}
extension CIImage {
var transparent: CIImage? {
inverted?.blackTransparent
}
var inverted: CIImage? {
guard let invertedColorFilter = CIFilter(name: "CIColorInvert") else { return nil }
invertedColorFilter.setValue(self, forKey: "inputImage")
return invertedColorFilter.outputImage
}
var blackTransparent: CIImage? {
guard let blackTransparentFilter = CIFilter(name: "CIMaskToAlpha") else { return nil }
blackTransparentFilter.setValue(self, forKey: "inputImage")
return blackTransparentFilter.outputImage
}
func tinted(using color: UIColor) -> CIImage? {
guard
let transparentQRImage = transparent,
let filter = CIFilter(name: "CIMultiplyCompositing"),
let colorFilter = CIFilter(name: "CIConstantColorGenerator") else { return nil }
let ciColor = CIColor(color: color)
colorFilter.setValue(ciColor, forKey: kCIInputColorKey)
let colorImage = colorFilter.outputImage
filter.setValue(colorImage, forKey: kCIInputImageKey)
filter.setValue(transparentQRImage, forKey: kCIInputBackgroundImageKey)
return filter.outputImage
}
func combined(with image: CIImage) -> CIImage? {
guard let combinedFilter = CIFilter(name: "CISourceOverCompositing") else { return nil }
let centerTransform = CGAffineTransform(translationX: extent.midX - (image.extent.size.width / 2), y: extent.midY - (image.extent.size.height / 2))
combinedFilter.setValue(image.transformed(by: centerTransform), forKey: "inputImage")
combinedFilter.setValue(self, forKey: "inputBackgroundImage")
return combinedFilter.outputImage!
}
}
经过一番调查我发现了这个扩展方法
extension UIImage {
static func make(qrcode string: String) -> UIImage? {
guard let data = string.data(using: String.Encoding.ascii) else { return nil }
var uiImage: UIImage?
if let filter = CIFilter(
name: "CIQRCodeGenerator",
parameters: ["inputMessage": data, "inputCorrectionLevel": "Q"]
) {
guard
let outputImage = filter.outputImage,
let cgImage = CIContext().createCGImage(outputImage, from: outputImage.extent)
else {
return nil
}
let scaleFactor: CGFloat = 12
let size = CGSize(
width: outputImage.extent.width * scaleFactor,
height: outputImage.extent.height * scaleFactor
)
UIGraphicsBeginImageContext(size)
if let context = UIGraphicsGetCurrentContext() {
context.interpolationQuality = .none
context.draw(cgImage, in: CGRect(origin: .zero, size: size))
uiImage = UIGraphicsGetImageFromCurrentImageContext()
}
UIGraphicsEndImageContext()
}
return uiImage
}
}
现在可以工作了,但我无法使其背景透明,我想如果不将 UIImage 转换为 CIImage 是不可能的。但当我这样做时,图像将不会渲染。可能会出现什么问题?
这是在iOS 17中实现所需的高质量透明背景二维码图像的更新代码,希望它能解决您的疑问。
更新代码:
extension UIImage {
static func make(qrcode string: String, tint color: UIColor) -> UIImage? {
guard let qrCIImage = string.qrCIImage else { return nil }
if let tintedCIImage = qrCIImage.tinted(using: color),
let cgImage = CIContext().createCGImage(tintedCIImage, from: tintedCIImage.extent) {
return UIImage(cgImage: cgImage)
}
return nil
}
}
extension String {
var qrCIImage: CIImage? {
guard let qrFilter = CIFilter(name: "CIQRCodeGenerator") else { return nil }
let qrData = data(using: String.Encoding.ascii)
qrFilter.setValue(qrData, forKey: "inputMessage")
let qrTransform = CGAffineTransform(scaleX: 12, y: 12)
return qrFilter.outputImage?.transformed(by: qrTransform)
}
var transparentQRImage: CIImage? {
return qrCIImage?.transparent
}
}
extension CIImage {
var transparent: CIImage? {
return inverted?.blackTransparent
}
var inverted: CIImage? {
guard let invertedColorFilter = CIFilter(name: "CIColorInvert") else { return nil }
invertedColorFilter.setValue(self, forKey: "inputImage")
return invertedColorFilter.outputImage
}
var blackTransparent: CIImage? {
guard let blackTransparentFilter = CIFilter(name: "CIMaskToAlpha") else { return nil }
blackTransparentFilter.setValue(self, forKey: "inputImage")
return blackTransparentFilter.outputImage
}
func tinted(using color: UIColor) -> CIImage? {
guard
let transparentQRImage = transparent,
let filter = CIFilter(name: "CIMultiplyCompositing"),
let colorFilter = CIFilter(name: "CIConstantColorGenerator") else { return nil }
let ciColor = CIColor(color: color)
colorFilter.setValue(ciColor, forKey: kCIInputColorKey)
let colorImage = colorFilter.outputImage
filter.setValue(colorImage, forKey: kCIInputImageKey)
filter.setValue(transparentQRImage, forKey: kCIInputBackgroundImageKey)
return filter.outputImage
}
}
使用方法:
class ViewController: UIViewController {
@IBOutlet weak var imgview: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .yellow
// Generate a QR code for a sample string
let sampleString = "Hello, QR Code!"
if let qrCodeImage = UIImage.make(qrcode: sampleString, tint: UIColor.black) {
imgview.image = qrCodeImage
}
}
}