I had extension methods which I used to transform QR codes into images. Everything was working flawlessly until iOS 17 update. They are not rendered now at all.
Extension that I was using before 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
}
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!
}
}
After some research I found this extension method
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
}
}
I've managed to make it work now, but I'm unable to make its background transparent. I believe this is impossible without transforming UIImage to CIImage. However, when I do that, the image won't render. What could be the issue?
This is the updated code to achieve the desired high-quality, transparent background QR code image in iOS 17, hope it solves your query.
Updated Code:
How to use it: