A QR Code is used a lot to share content or to add a new user in apps like Twitter and Snapchat. Since iOS 11 it’s possible for users to scan a QR code with the built-in camera app. This makes it even nicer to integrate a QR code in your own app.
Generating a QR code in iOS
Generating a QR code in iOS is easier than ever since the introduction of the CIQRCodeGenerator
Core Image filter. It generates a plain black and white QR for the given input string.
guard let qrFilter = CIFilter(name: "CIQRCodeGenerator") else { return nil }
let qrData = absoluteString.data(using: String.Encoding.ascii)
qrFilter.setValue(qrData, forKey: "inputMessage")
let qrTransform = CGAffineTransform(scaleX: 12, y: 12)
let qrImage = qrFilter.outputImage?.transformed(by: qrTransform)
This QR code works already, but it is not really appealing and it is probably not aligned with the awesome design of your own app. Therefore, it would be really nice if we could customize it to fit our app design.
Changing the color of a QR code
The first step in customizing the QR code is to change the color. We do this in three steps:
- Invert the colors black and white
- Mask the black color to transparent
- Change the color
To make this code easier to use we create an extension on CIImage
.
extension CIImage {
/// Inverts the colors and creates a transparent image by converting the mask to alpha.
/// Input image should be black and white.
var transparent: CIImage? {
return inverted?.blackTransparent
}
/// Inverts the colors.
var inverted: CIImage? {
guard let invertedColorFilter = CIFilter(name: "CIColorInvert") else { return nil }
invertedColorFilter.setValue(self, forKey: "inputImage")
return invertedColorFilter.outputImage
}
/// Converts all black to transparent.
var blackTransparent: CIImage? {
guard let blackTransparentFilter = CIFilter(name: "CIMaskToAlpha") else { return nil }
blackTransparentFilter.setValue(self, forKey: "inputImage")
return blackTransparentFilter.outputImage
}
/// Applies the given color as a tint color.
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!
}
}
A QR code is often linking to a URL. Therefore, it’s nice to create an extension on URL
as well.
extension URL {
/// Creates a QR code for the current URL in the given color.
func qrImage(using color: UIColor) -> CIImage? {
return qrImage?.tinted(using: color)
}
/// Returns a black and white QR code for this URL.
var qrImage: CIImage? {
guard let qrFilter = CIFilter(name: "CIQRCodeGenerator") else { return nil }
let qrData = absoluteString.data(using: String.Encoding.ascii)
qrFilter.setValue(qrData, forKey: "inputMessage")
let qrTransform = CGAffineTransform(scaleX: 12, y: 12)
return qrFilter.outputImage?.transformed(by: qrTransform)
}
}
Which can be used as followed:
let swiftLeeOrangeColor = UIColor(red:0.93, green:0.31, blue:0.23, alpha:1.00)
let qrURLImage = URL(string: "https://www.avanderlee.com")?.qrImage(using: swiftLeeOrangeColor)
A QR code with a custom logo
To make it a really outstanding QR we can add our own custom logo. As a QR code has a certain error correction it allows us to replace a certain part of it with our custom logo. To do this, we create another extension on CIImage
which takes our logo and returns a new CIImage
which is a combination of the QR and our logo.
extension CIImage {
/// Combines the current image with the given image centered.
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!
}
}
Combined with our updated URL
extension we can create a nice new QR code including our own custom logo.
extension URL {
/// Creates a QR code for the current URL in the given color.
func qrImage(using color: UIColor, logo: UIImage? = nil) -> CIImage? {
let tintedQRImage = qrImage?.tinted(using: color)
guard let logo = logo?.cgImage else {
return tintedQRImage
}
return tintedQRImage?.combined(with: CIImage(cgImage: logo))
}
}
let swiftLeeOrangeColor = UIColor(red:0.93, green:0.31, blue:0.23, alpha:1.00)
let swiftLeeLogo = UIImage(named: "swiftlee_qr_logo.png")!
let qrURLImage = URL(string: "https://www.avanderlee.com")?.qrImage(using: swiftLeeOrangeColor, logo: swiftLeeLogo)
A Playground to play around
A complete version of this code can be found on Github here. It’s a Playground to allow you to play around and create your own custom QR code.
If you want to read more about the QR code filter or any other Core Image filters, check out the Apple Documentation