概要
SwiftUIでQRコードを表示する必要があり、調べた内容になります。
UIKitと全く同じだと若干ハマりどころもあるため、備忘録として残しておきます。
環境
- Swift: 5.2.2
- Xcode: 11.4.1
QRCodeの画像を作成
import CoreImage import UIKit class QRCode { static func makeQRImage(_ input: String) -> UIImage { let inputData = input.data(using: .utf8)! // NOTE:誤り訂正レベルはとりあえず「Q」を指定 let qrFilter = CIFilter(name: "CIQRCodeGenerator", parameters: ["inputMessage": inputData, "inputCorrectionLevel": "Q"]) let ciImage = qrFilter!.outputImage! // NOTE: 元のCIImageは小さいので任意のサイズに拡大 let sizeTransform = CGAffineTransform(scaleX: 10, y: 10) let scaledCiImage = ciImage.transformed(by: sizeTransform) // NOTE: CIImageをそのまま変換するとImageで表示されないため一度CGImageに変換してからUIImageに変換する let context = CIContext() let qrCgImage = context.createCGImage(scaledCiImage, from: scaledCiImage.extent)! return UIImage(cgImage: qrCgImage) } }
まず、QRコードを作成します。(簡単のため、エラー処理などは除外しています)
SwiftでQRコードを作成するには、CoreImage
のCIFilerを使います。
let qr = CIFilter(name: "CIQRCodeGenerator", parameters: ["inputMessage": inputData, "inputCorrectionLevel": "Q"])
CiFilterに CIQRCodeGenerator
という「name」を渡すことでQRコードのCIImageを作成することができます。
「parameters」には、inputMessage
にQRCodeに変換したい文字列を Data
型のオブジェクトとしてわたします。
inputCorrectionLevel
は、「誤り訂正レベル」というQRコードの仕様で「L」、「M」、「Q」、「H」のいずれかを指定します。
let ciImage = qrFilter!.outputImage! // NOTE: 元のCIImageは小さいので任意のサイズに拡大 let sizeTransform = CGAffineTransform(scaleX: 10, y: 10) let scaledCiImage = ciImage.transformed(by: sizeTransform)
作成したCIFilterからCIImageを取り出し、拡大処理をしています。
(そのままのCIImageは、かなりサイズが小さいので10倍位で300pt前後になると思います)
let context = CIContext() let qrCgImage = context.createCGImage(scaledCiImage, from: scaledCiImage.extent)! return UIImage(cgImage: qrCgImage)
最後に作成したCIImage
を一旦 CGImage
に変換して UIImage
に変換します。
ここが、UIKitの場合と異なる箇所です。
UIKitを使う場合、画像の表示には UIImageView
を使うのが一般的ですが、UIImageViewはCIImageから変換したUIImageを渡すだけで描画することができます。
しかし、SwiftUIの Image
コンポーネントは、CIImageから変換したUIImageだと正しく描画することができません。(エラーにならないが描画されない)
(詳細な理由は理解できていないのですが、 SwiftUIのImageのイニシャライザに、UIImageとCIImageはあるがCGImageがないので描画方式に違いがあるのかもしれません)
そのため、一旦CGImageに変換したものを再度UIImageに変換します。
(前述の通り、Image
コンポーネントは、直接 CGImage を受け取るイニシャライザをもっているため、UIImageに変換せず、CGImageを返すだけでも問題ありません。)
View側の処理
struct ContentView: View { var body: some View { VStack { Image(uiImage: QRCode.makeQRImage("test")) } } }
View側は Image
コンポーネントに作成した UIImageを渡すことでQRコードを表示できます。
所感
微妙なハマりどころですが、UIKitからコード移植する場合などは注意が必要かもしれません。
この辺のノウハウがもう少し世の中に公開されてくるとSwifUIの普及も進んでくるような気がします。