◻️

【SwiftUI】QR コードを生成して Image に表示する

2024/10/20に公開

はじめに

iOS で QR コードを生成し、SwiftUI.Image に表示する方法を簡単にまとめました。

環境

  • Swift 5
  • iOS Minimum Deployment: 18.0

本記事で使用するサンプルコード

https://github.com/mtkmr/QRCodeGenerator

QR コードを表示するビュー

先に QR コードを表示するビュー側の動作とコードを示します。
今回は、TextField に URL などのテキストを入力し、 "Create" ボタンで QR コードを作成して表示するシンプルなサンプルアプリをベースに説明します。


Create ボタンをタップして、QR コードを表示する

QRCodeView.swift
import SwiftUI

struct QRCodeView: View {
    
    @State private var viewModel = QRCodeViewModel()
    @FocusState private var isFocused: Bool
    
    var body: some View {
        VStack(alignment: .center, spacing: 16) {
            TextField("Enter URL", text: $viewModel.urlString)
                .textFieldStyle(.roundedBorder)
                .focused($isFocused)
                .onSubmit {
                    isFocused = false
                }
            
            Button {
                // QR コードを生成する
                viewModel.generateQRCode()
            } label: {
                Text("Create")
                    .padding(8)
                    .fontWeight(.bold)
                    .background(Color.green)
                    .foregroundStyle(.white)
                    .clipShape(RoundedRectangle(cornerRadius: 16))
            }
            
            Spacer()

            if let qrCodeImage = viewModel.qrCodeImage {
                Image(uiImage: qrCodeImage)
                    // QR コードは正確なピクセル配置が重要であり、スケーリングや補完の影響を受けやすい
                    // 補完動作を .none に指定しないと、ぼやけた見た目になってしまう
                    .interpolation(.none)
                    .resizable()
                    .frame(width: 300, height: 300)
                
                Spacer()
            }
            
        }
        .padding()
    }
}
  • interpolation(_:) モディファイアは、画像の補完設定です。QR コードのようにピクセルごとの精度が重要な場合には、.none に設定して補完を無効にしないと。Image を risezable する際に補完が働き、QR コードがぼやけて表示されてしまいます。

QR コードの生成

次に、QR コードを生成するロジックを担う ViewModel 側のコードを見ていきましょう。

QRCodeViewModel.swift
import CoreImage
import UIKit

@MainActor
@Observable
final class QRCodeViewModel {
    
    var urlString = ""
    var qrCodeImage: UIImage?
    
    private let context = CIContext()
    
    func generateQRCode() {
        // 入力した文字列を Data に変換
        guard let data = urlString.data(using: .utf8) else { return }
        // CIFilter を使って QR コードの CIImage を生成する
        let qr = CIFilter(
            name: "CIQRCodeGenerator",
            parameters: [
                "inputMessage": data,
                "inputCorrectionLevel": "M",
            ]
        )
        // CIImage のリサイズ
        let transform = CGAffineTransform(scaleX: 1, y: 1)
        guard let outputImage = qr?.outputImage?.transformed(by: transform),
              // CIImage から CGImage にレンダリング
              let cgImage = context.createCGImage(outputImage, from: outputImage.extent) else { return }
        // CGImage から UIImage に変換
        self.qrCodeImage = UIImage(cgImage: cgImage)
    }
}
  • CoreImage フレームワーク
    • QR コードを生成するためのフィルター機能が含まれています。CIFilter クラスを使って、 QR コードを生成します。
  • CIQRCodeGenerator の使用
    • QR コード生成には CIQRCodeGenerator を使用します。これは CICategoryGenarator の一つで、テキストや URL などを QR コードに変換してくれるものです。
    • inputMessage パラメータには、QR コードとしてエンコードしたいデータを渡します。
    • inputCorrectionLevel パラメータには、誤り訂正機能の復元率を指定します。デフォルトでは M となっています。
  • CIImageCGImageUIImage の変換
    • CIImage はあくまで画像データの「レシピ」であり、例えば、フィルターを使用したり、画像にエフェクトをかけたりといった処理を行うための中間的な形式です。まだレンダリングされていない状態であり、解像度情報が含まれていないため、そのまま UIImage に変換して SwiftUI.Image に渡してもうまく表示できないことがあります。
    • CGImage は、解像度やピクセルデータが含まれた、最終的なレンダリング結果です。よって描画可能な画像形式なので、これを表示用途に最適化した UIImage に変換して表示しています。

まとめ

CIFilterCIQRCodeGenerator を使用して、任意のテキストを QR コード画像に変換して表示するコードを見てきました。QR コードがぼやけないようにしたり、CIImage から UIImage までの変換に注意する必要はあるものの、非常に簡単に QR コードを生成・表示できることが分かりました。

参考文献

Discussion