When I was developing a QR code scanner and generator as a part of one of my apps, I was a little bit confused. I mean it is really simple to generate QR code in Swift 3.1 like so:
//First convert your string to data let stringData = someString.data(using: String.Encoding.utf8) //Then use CIFilter to create a CIImage of the QR Code let filter = CIFilter(name: "CIQRCodeGenerator") filter?.setValue(stringData, forKey: "inputMessage") filter?.setValue("H", forKey: "inputCorrectionLevel") guard let ciImage = filter?.outputImage else { return nil } //Finally create the well known UIImage let image = UIImage(ciImage: ciImage)
But when I tried to copy the image to the general UIPasteboard or export it via UIActivityViewController, it would just not work. 🤔
Why? I have no idea! As a good programmer, I asked the guy who knows everything: Google. 🤓
It turned out I wasn’t the only one who had this issue (who has seen that coming?). Though, people did not create UIImages from CIImages, so suggestions like “Save your UIImage to a file and then you can share it.” would not only not work because UIImagePNGRepresentation(image:)
returns nil for UIImages
from CIImage
but I also did not want to store a file in my documents directory.
I mean why would I? I want to create QR codes with Swift on the fly and then share them via UIPasteboard
or UIActivityViewController
. That can’t be that hard, right? 💁
And actually it isn’t hard at all. As suggested by this kind person, all we have to do is to convert the CIImage
to a CGImage
before creating the UIImage
. Woa, so many images. 😨
So here is the final product. A nice function to turn Strings into QR codes in Swift 3.1. I also added a size argument so you can get nice and crisp QR codes of any size you desire. You could for example pass the size of an UIImageView
to this function to get a QR code which exactly fits its size.
func generateBarcode(text: String?, size: CGFloat) -> UIImage? { //Make sure there is a String to work with guard let text = text else { return nil } //Convert string to data let stringData = text.data(using: String.Encoding.utf8) //Generate CIImage let filter = CIFilter(name: "CIQRCodeGenerator") filter?.setValue(stringData, forKey: "inputMessage") filter?.setValue("H", forKey: "inputCorrectionLevel") guard let ciImage = filter?.outputImage else { return nil } //Scale image to proper size let scale = size / ciImage.extent.size.width let transform = CGAffineTransform(scaleX: scale, y: scale) let scaledCIImage = ciImage.applying(transform) //Convert to CGImage let ciContext = CIContext() guard let cgImage = ciContext.createCGImage(scaledCIImage, from: scaledCIImage.extent) else { return nil } //Finally return the UIImage return UIImage(cgImage: cgImage) }
Yeah, done! Time to celebrate and have some cake. 🎉 🎂 🎊
But wait, what if we need to generate QR codes in various places in our App? Well, in this case, lets just make this an extension on String for our own convenience and reusability. Or should we make it a UIImage initializer? 🤔
I don’t know. Maybe you can tell me which one is better (you know, down there, in the comments 🙃). Anyways, here is the String extension:
extension String { func qrCode(withSize size: CGFloat) -> UIImage? { //Get self as data let stringData = self.data(using: String.Encoding.utf8) //Generate CIImage let filter = CIFilter(name: "CIQRCodeGenerator") filter?.setValue(stringData, forKey: "inputMessage") filter?.setValue("H", forKey: "inputCorrectionLevel") guard let ciImage = filter?.outputImage else { return nil } //Scale image to proper size let scale = size / ciImage.extent.size.width let transform = CGAffineTransform(scaleX: scale, y: scale) let scaledCIImage = ciImage.applying(transform) //Convert to CGImage let ciContext = CIContext() guard let cgImage = ciContext.createCGImage(scaledCIImage, from: scaledCIImage.extent) else { return nil } //Finally return UIImage return UIImage(cgImage: cgImage) } }
And here the UIImage initializer:
extension UIImage { convenience init?(qrCode: String?, size: CGFloat) { //Fail if there is no text guard let qrCode = qrCode else { return nil } //Get self as data let stringData = qrCode.data(using: String.Encoding.utf8) //Generate CIImage let filter = CIFilter(name: "CIQRCodeGenerator") filter?.setValue(stringData, forKey: "inputMessage") filter?.setValue("H", forKey: "inputCorrectionLevel") guard let ciImage = filter?.outputImage else { return nil } //Scale image to proper size let scale = size / ciImage.extent.size.width let transform = CGAffineTransform(scaleX: scale, y: scale) let scaledCIImage = ciImage.applying(transform) //Convert to CGImage let ciContext = CIContext() guard let cgImage = ciContext.createCGImage(scaledCIImage, from: scaledCIImage.extent) else { return nil } //Initialize with CGImage self.init(cgImage: cgImage) } }
And last but not least, here’s how you can finally generate a QR code from String and copy it to UIPasteboard on iOS with Swift 3.1 (what a wonderful, SEO optimized sentence ✌️):
let textToCreateQRCodeFor = "Make me a QR code" let qrCodeImage = UIImage(qrCode: textToCreateQRCodeFor, size: 250.0) UIPasteboard.general.image = qrCodeImage
Thank you very much!
I was trying to share the generated QR Code using UIActivityViewController and as you pointed out converting the CIImage or UIImage to Data was failing and it was not working.
But with your suggested intermediate step of first converting CIImage into CGImage, it is working fine.
Thanks once again.
I’m happy that I could help. 🙂 I think I ran into the exact same issue as you did and luckily found this solution.