42

Is there a standard way to generate a QR code and attach it to a mail item from iOS client app (no server code)?

thesquaregroot
  • 1,414
  • 1
  • 21
  • 35
user1561181
  • 458
  • 1
  • 5
  • 6

8 Answers8

100

Since iOS 7, you can use a Core Image filter to generate QR images. See the final tip here:

- (CIImage *)createQRForString:(NSString *)qrString {
    NSData *stringData = [qrString dataUsingEncoding: NSISOLatin1StringEncoding];

    CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
    [qrFilter setValue:stringData forKey:@"inputMessage"];

    return qrFilter.outputImage;
}
zoul
  • 102,279
  • 44
  • 260
  • 354
quarac
  • 3,454
  • 3
  • 21
  • 25
  • 2
    The image is blurry. How do i fix? – Royston Yinkore Apr 21 '14 at 16:41
  • 1
    Look in the link for the function: createNonInterpolatedUIImageFromCIImage – quarac Apr 30 '14 at 22:38
  • 1
    You should be using `NSISOLatin1StringEncoding` rather than UTF-8 according to the docs. – T Blank Mar 08 '15 at 05:28
  • Thanks Travis, answer updated with NSISOLatin1StringEncoding, docs are in https://developer.apple.com/library/mac/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html#//apple_ref/doc/filter/ci/CIQRCodeGenerator – quarac Mar 09 '15 at 10:08
  • 24
    if the qr code is blurry, try this. CIImage *image = [self createQRForString:qrString]; CGAffineTransform transform = CGAffineTransformMakeScale(5.0f, 5.0f); // Scale by 5 times along both dimensions CIImage *output = [image imageByApplyingTransform: transform]; – kevinl May 13 '15 at 18:24
  • @kevinl: your code work fine on the simulator. But for the real devices it's still blur. What should I do to make it clear? – vichhai Dec 07 '15 at 02:23
  • 2
    I use kevin's solution to make image not blur, it works on real device. – haxpor Mar 27 '18 at 19:24
  • @kevinl To add on to what Kevin has said, you can also apply, to any imageView that may be scaling arbitrarily (like a scrollview zoom), a nearest neighbor interpolation since all visual data in a QR Code is orthogonal, this will cause sharp/crisp edges when scaling, set the .layer.minificationFilter and .layer.magnificationLayer to `kCAFilterNearest` – Albert Renshaw Oct 19 '19 at 21:27
  • 1
    Note: Make Sure you convert the CIImage to a UIImage using CGContext, if you just use `UIImage imageWithCIImage:` you will get a bmp formatted image which causes `contentMode` to fail on certain architectures. For example, on my iPhone-11 (arm64) aspectFit worked with the UIImage from CIImage but on my iPhone-X (armv7s) it failed and fell back to scaleToFill (which makes QR codes not work). Detailed Explanation here: https://stackoverflow.com/a/15886422/2057171 – Albert Renshaw Jun 03 '20 at 22:43
  • 1
    @AlbertRenshaw Thank you! Without doing that I wasn't able to see any image at all inside of a SwiftUI view. – Kilian Sep 23 '21 at 20:07
27

For Obj-C version that perfectly works for me, I've mixed answers पवन and Teja Kumar Bethina:

NSString *qrString = @"My string to encode";
NSData *stringData = [qrString dataUsingEncoding: NSUTF8StringEncoding];

CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
[qrFilter setValue:stringData forKey:@"inputMessage"];
[qrFilter setValue:@"H" forKey:@"inputCorrectionLevel"];

CIImage *qrImage = qrFilter.outputImage;
float scaleX = self.qrImageView.frame.size.width / qrImage.extent.size.width;
float scaleY = self.qrImageView.frame.size.height / qrImage.extent.size.height;

qrImage = [qrImage imageByApplyingTransform:CGAffineTransformMakeScale(scaleX, scaleY)];

self.qrImageView.image = [UIImage imageWithCIImage:qrImage
                                             scale:[UIScreen mainScreen].scale
                                       orientation:UIImageOrientationUp];
Mike Demidov
  • 1,117
  • 11
  • 20
18

Using Swift 2

import UIKit
import CoreImage

func createQRFromString(str: String) -> CIImage? {
    let stringData = str.dataUsingEncoding(NSUTF8StringEncoding)
    
    let filter = CIFilter(name: "CIQRCodeGenerator")
    
    filter?.setValue(stringData, forKey: "inputMessage")
    
    filter?.setValue("H", forKey: "inputCorrectionLevel")
    
    return filter?.outputImage
}

if let img = createQRFromString("Hello world program created by someone") {
    let somImage = UIImage(CIImage: img, scale: 1.0, orientation: UIImageOrientation.Down)
}

enter image description here

Swift 3.0

import UIKit
import CoreImage

func createQRFromString(_ str: String) -> CIImage? {
        let stringData = str.data(using: String.Encoding.utf8)
        
        let filter = CIFilter(name: "CIQRCodeGenerator")
        
        filter?.setValue(stringData, forKey: "inputMessage")
        
        filter?.setValue("H", forKey: "inputCorrectionLevel")
        
        if let img = createQRFromString("Hello world program created by someone") {
            let somImage = UIImage(ciImage: img, scale: 1.0, orientation: UIImageOrientation.down)
        }
        
        return filter?.outputImage
    }

if let img = createQRFromString("Hello world program created by someone") {
            let somImage = UIImage(ciImage: img, scale: 1.0, orientation: UIImageOrientation.down)
        }

Swift 4.2

private func createQRFromString(str: String) -> CIImage? {
        let stringData = str.data(using: .utf8)
        
        let filter = CIFilter(name: "CIQRCodeGenerator")
        filter?.setValue(stringData, forKey: "inputMessage")
        filter?.setValue("H", forKey: "inputCorrectionLevel")
        
        return filter?.outputImage
    }
    
    var qrCode: UIImage? {
        if let img = createQRFromString(str: "Hello world program created by someone") {
            let someImage = UIImage(
                ciImage: img,
                scale: 1.0,
                orientation: UIImage.Orientation.down
            )
            return someImage
        }
        
        return nil
    }

Xcode 12.4 or Swift version >= 5.2

import UIKit
import CoreImage

func createQRFromString(str: String) -> CIImage? {
  let stringData = str.data(using: .utf8)
  
  let filter = CIFilter(name: "CIQRCodeGenerator")
  
  filter?.setValue(stringData, forKey: "inputMessage")
  
  filter?.setValue("H", forKey: "inputCorrectionLevel")
  
  return filter?.outputImage
}

if let img = createQRFromString(str: "Hello world program created by someone") {
  let somImage = UIImage(ciImage: img, scale: 1.0, orientation: .down)
}
Pawan Sharma
  • 3,199
  • 1
  • 25
  • 32
14

It has been a while since this question was asked and a number of almost perfect answers have been given already. However I had to tweak and combine several answers to get it working perfectly for AppleTV 4K, iPhone X and iPadPro using Xcode 9.2 in 2018. Here's the code if anyone needs it.

@IBOutlet weak var qrCodeBox: UIImageView!

func createQRFromString(_ str: String, size: CGSize) -> UIImage {
    let stringData = str.data(using: .utf8)

  let qrFilter = CIFilter(name: "CIQRCodeGenerator")!
  qrFilter.setValue(stringData, forKey: "inputMessage")
  qrFilter.setValue("H", forKey: "inputCorrectionLevel")

  let minimalQRimage = qrFilter.outputImage!
  // NOTE that a QR code is always square, so minimalQRimage..width === .height
  let minimalSideLength = minimalQRimage.extent.width

  let smallestOutputExtent = (size.width < size.height) ? size.width : size.height
  let scaleFactor = smallestOutputExtent / minimalSideLength
  let scaledImage = minimalQRimage.transformed(
    by: CGAffineTransform(scaleX: scaleFactor, y: scaleFactor))

  return UIImage(ciImage: scaledImage, 
                 scale: UIScreen.main.scale, 
                 orientation: .up)
}

override func viewDidLoad() {
    super.viewDidLoad()
    let myQRimage = createQRFromString("https://www.apple.com", 
                      size: qrCodeBox.frame.size)
    qrCodeBox.image = myQRimage
}
Fred Truter
  • 667
  • 4
  • 10
  • There's actually a small bug here. You have to multiply `scaledImage` by `UIScreen.main.scale` or the final UIImage will be too small. For example on an iPhone X with screen scale of 3.0, if you try and make a 300x300 QR code the final UIImage will be 100x100 if you use this code as-is. – Ben Baron Mar 14 '19 at 00:33
4

Code to generate QR image in Swift 2.0.

let reqStr = “string to convert as QR code”
let data = reqStr.dataUsingEncoding(NSISOLatin1StringEncoding, allowLossyConversion: false)

let filter = CIFilter(name: "CIQRCodeGenerator")
filter!.setValue(data, forKey: "inputMessage")

let qrImage:CIImage = filter!.outputImage!

//qrImageView is a IBOutlet of UIImageView        
let scaleX = qrImageView.frame.size.width / qrImage.extent.size.width
let scaleY = qrImageView.frame.size.height / qrImage.extent.size.height

let resultQrImage = qrImage.imageByApplyingTransform(CGAffineTransformMakeScale(scaleX, scaleY))
qrImageView.image = UIImage(CIImage: resultQrImage)
Teja Kumar Bethina
  • 3,486
  • 26
  • 34
0

...which will let me generate links...

First, you need to find a short link service, e.g. bit.ly or goo.by, to make a long link shorter and thus reduce QR Code size.

To perform this task automatically you will have to use some web service.

...then generate a QR code...

ZXing is a popular open source QR Code generator also available for iOS

Avivo
  • 27
  • 5
0

If you don't mind using public api, here is an easy 10 seconds method. http://goqr.me/api

Just fill in the data parameter, and load the image from the response. Cheers.

Sharukh Mastan
  • 1,491
  • 18
  • 17
0

I created a NSString Category for Obj-C, taking Mike Demidov awesome answer

NString+GGQRCode.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface NSString (GGQRCode)

-(UIImage *)qrCodeImage:(CGFloat)width height:(CGFloat)height;
-(UIImage *)qrCodeImage:(CGFloat)width height:(CGFloat)height scale:(CGFloat)scale orientation:(UIImageOrientation)orientation;

@end

NString+GGQRCode.m

#import "NSString+GGQRCode.h"

@implementation NSString (GGQRCode)

-(UIImage *)qrCodeImage:(CGFloat)width height:(CGFloat)height
{
    return [self qrCodeImage:width height:height scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp];
}

-(UIImage *)qrCodeImage:(CGFloat)width height:(CGFloat)height scale:(CGFloat)scale orientation:(UIImageOrientation)orientation
{
    NSData *stringData = [self dataUsingEncoding: NSUTF8StringEncoding];

    CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
    [qrFilter setValue:stringData forKey:@"inputMessage"];
    [qrFilter setValue:@"H" forKey:@"inputCorrectionLevel"];

    CIImage *qrImage = qrFilter.outputImage;
    float scaleX = width / qrImage.extent.size.width;
    float scaleY = height / qrImage.extent.size.height;

    qrImage = [qrImage imageByApplyingTransform:CGAffineTransformMakeScale(scaleX, scaleY)];

    return [UIImage imageWithCIImage:qrImage scale:scale orientation:orientation];
}

@end
gorkem
  • 500
  • 6
  • 8