Swift Generate Qr Code with Transparent Background

Swift Generate Qr Code with Transparent Background

in my swift 2 app i can generate a QR Code like this:

let data = "1234567890".dataUsingEncoding(NSISOLatin1StringEncoding, allowLossyConversion: false)
let filter = CIFilter(name: "CIQRCodeGenerator")
filter!.setValue(data, forKey: "inputMessage")
filter!.setValue("Q", forKey: "inputCorrectionLevel")
qrcodeImage = filter!.outputImage
let transformedImage = qrcodeImage.imageByApplyingTransform(CGAffineTransformMakeScale(150, 150))
QRCodeImage.image = UIImage(CIImage: transformedImage)

but my QR Code get an white background image, but i would like to have an transparent background.

i tried something like this:

QRCodeImage.backgroundColor = UIColor.clearColor()

but this doesn't work. any idea ? :)

3

8 Answers

It seems like the CIQRCodeGenerator filter always uses black and white.

You can pass the output into a CIMaskToAlpha filter to convert it to transparent:

And first you might want to use CIColorInvert to swap white & black.

2

For transparent background you should (use your instincts and) try like this ,

//Create a CIFalseColor Filter
let colorFilter: CIFilter = CIFilter(name: "CIFalseColor")!

colorFilter.setDefaults()

colorFilter.setValue(yourQRFilter.outputImage!, forKey: "inputImage")

//Then set the background colour like this,
let transparentBG: CIColor = CIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0)

colorFilter.setValue(qrColor, forKey: "inputColor0")

colorFilter.setValue(transparentBG, forKey: "inputColor1")

outputImage = colorFilter.outputImage!

This should yield you QRCode image with transparent background, I hope.

I think that the key will be on the alpha of the background. You can set or reduce alpha, reducing or deleting the transparency. Try something like this:

QRCodeImage.backgroundcolor = UIColor(white: 1, alpha: 0.5)

Hope it helps.

4

this is my code :

func QRImageFromData(_ data: Data) -> UIImage? {

    let filter = CIFilter(name: "CIQRCodeGenerator")
    filter?.setValue(data, forKey: "inputMessage")
    //filter?.setValue("H", forKey: "inputCorrectionLevel")

    //change qrcode color : #1e3259
    let filterFalseColor = CIFilter(name: "CIFalseColor")
    filterFalseColor?.setDefaults()
    filterFalseColor?.setValue(filter?.outputImage, forKey: "inputImage")
    // convert method
    let cgColor: CGColor? = UIColor(hex: "#1e3259")?.cgColor
    let qrColor: CIColor = CIColor(cgColor: cgColor!)
    let transparentBG: CIColor = CIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0)
    filterFalseColor?.setValue(qrColor, forKey: "inputColor0")
    filterFalseColor?.setValue(transparentBG, forKey: "inputColor1")


    if let image = filterFalseColor?.outputImage {
        let transform = CGAffineTransform(scaleX: 5.0, y: 5.0)
        return UIImage(ciImage: image.applying(transform),
            scale: 1.0,
            orientation: UIImageOrientation.up)
    } else {
        return nil
    }

}

I obtained a qr with a transparent background following this tutorial.

Add these extensions:

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!
    }
}


extension URL {

    func qrImage(using color: UIColor) -> UIImage? {
        let ciImage = qrCIImage?.tinted(using: color)
        return ciImage != nil ? UIImage(ciImage: ciImage!) : nil
    }

    var qrImage: UIImage? {
        return UIImage(ciImage: qrCIImage!)
    }

    /// Creates a QR code for the current URL in the given color.
    func qrCIImage(using color: UIColor) -> CIImage? {
        return qrCIImage?.tinted(using: color)
    }

    /// Returns a black and white QR code for this URL.
    var qrCIImage: 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)
    }

}

And then use like this:

let myChosenColor = UIColor(red:0.93, green:0.31, blue:0.23, alpha:1.00)
let qrURLImage = URL(string: "")?.qrImage(using: myChosenColor)
myQrImageView.image = qrURLImage

Inside myQrImageView.image you will see a qr image with a transparent background:

In addition to answer of @jtbandes i have wrote some code so it would help others as well

extension UIImage {

    func transparentBackground() -> UIImage? {
        let context = CIContext(options: nil)
        let filter = CIFilter(name: "CIMaskToAlpha")
        filter?.setDefaults()
        filter?.setValue(self.ciImage, forKey: kCIInputImageKey)
        if let output = filter?.outputImage,
           let imageRef = context.createCGImage(output, from: output.extent) {
            return UIImage(cgImage: imageRef)
        }
        return nil
    }

    func invertColor() -> UIImage? {
        let filter = CIFilter(name: "CIColorInvert")
        filter?.setDefaults()
        filter?.setValue(self.ciImage, forKey: kCIInputImageKey)
        if let output = filter?.outputImage {
            return UIImage(ciImage: output)
        }
        return nil
    }

}

and use it like this

yourUIImage.invertColor()?.transparentBackground()

the transparentBackground() function will remove black color and leave the white where as invertColor() will will swap black with white

extension UIImage {
    func alpha(_ value: CGFloat) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(size, false, scale)
        draw(at: CGPoint.zero, blendMode: .normal, alpha: value)
        let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return image
    }
}

A ready solution in case you're looking for:

enum GeneratorType {
  case qrCode, barCode, aztecCode
  
  var filterName: String {
    switch self {
    case .aztecCode:
      return "CIAztecCodeGenerator"
    case .barCode:
      return "CICode128BarcodeGenerator"
    case .qrCode:
      return "CIQRCodeGenerator"
    }
  }
}

func generateCode(type: GeneratorType, text: String,
                  transform: CGAffineTransform = CGAffineTransform(scaleX: 32.0, y: 32.0),
                  fillColor: UIColor = UIColor.black,
                  backgroundColor: CIColor = CIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0)) -> UIImage? {
  guard let filter = CIFilter(name: type.filterName) else {
    return nil
  }
  
  filter.setDefaults()
  
  guard let data = text.data(using: String.Encoding.utf8) else {
    return nil
  }
  
  filter.setValue(data, forKey: "inputMessage")
  
  let filterFalseColor = CIFilter(name: "CIFalseColor")
  filterFalseColor?.setDefaults()
  filterFalseColor?.setValue(filter.outputImage, forKey: "inputImage")
  filterFalseColor?.setValue(CIColor(cgColor: fillColor.cgColor), forKey: "inputColor0")
  filterFalseColor?.setValue(backgroundColor, forKey: "inputColor1")
  
  
  guard let image = filterFalseColor?.outputImage else { return nil }
  
  return UIImage(ciImage: image.transformed(by: transform), scale: 1.0,
                 orientation: UIImage.Orientation.up)
}

Usage:

let qrImage = generateCode(type: .qrCode, text: "some text")

Peace!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Marcus Vance
Author

Marcus Vance

Marcus Vance is a cybersecurity auditor and technology writer dedicated to educating the public about online safety, data privacy regulations, enterprise security, and emerging cyber threats.