今回はSwiftUIでQRコードを生成して、ブランディングの為にQRコードの中心に可愛いアイコンを付ける方法を紹介したいと思います。
環境
- Xcode 14
- Swift 5.7
QRコードを生成する
QRコードを生成する構造体を作成しました。
QRCodeGenerator
import SwiftUI
struct QRCodeGenerator {
func generate(with inputText: String) -> UIImage? {
guard let qrFilter = CIFilter(name: "CIQRCodeGenerator")
else { return nil }
let inputData = inputText.data(using: .utf8)
qrFilter.setValue(inputData, forKey: "inputMessage")
// 誤り訂正レベルをHに指定
qrFilter.setValue("H", forKey: "inputCorrectionLevel")
guard let ciImage = qrFilter.outputImage
else { return nil }
// CIImageは小さい為、任意のサイズに拡大
let sizeTransform = CGAffineTransform(scaleX: 10, y: 10)
let scaledCiImage = ciImage.transformed(by: sizeTransform)
// CIImageだとSwiftUIのImageでは表示されない為、CGImageに変換
let context = CIContext()
guard let cgImage = context.createCGImage(scaledCiImage,
from: scaledCiImage.extent)
else { return nil }
return UIImage(cgImage: cgImage)
}
}
QRコード用のCIFilterからCIImageを作成する
guard let qrFilter = CIFilter(name: "CIQRCodeGenerator")
else { return nil }
let inputData = inputText.data(using: .utf8)
qrFilter.setValue(inputData, forKey: "inputMessage")
// 誤り訂正レベルをHに指定
qrFilter.setValue("H", forKey: "inputCorrectionLevel")
guard let ciImage = qrFilter.outputImage
else { return nil }
// CIImageは小さい為、任意のサイズに拡大
let sizeTransform = CGAffineTransform(scaleX: 10, y: 10)
let scaledCiImage = ciImage.transformed(by: sizeTransform)
CIFilter(name: "CIQRCodeGenerator")
でQRコード用のCIFilter
を生成することが出来ます。
引数として受け取っているinputText
をData型に変換して、qrFliter.setValue
でKey inputMessage
のvalue
にその値を指定します。
また、qrFliter.setValue
でKey inputCorrectionLevel
のvalue
に誤り補正レベルを指定します。
そして、qrFilter.outputImage
からQRコードのCIImage
を受け取っています。
そのままのCIImage
ではサイズが小さい為、任意のサイズにtransformed
を使用して拡大しています。
誤り補正レベルとは、
QRコードは汚れていたり破損していたりしていてもある程度自ら復元することが可能です。誤り訂正レベルは4段階存在し、高レベルであれば訂正能力は上がりますが、コードのサイズは大きくなります。
引用: 【iOS】SwiftでQRコードを表示して目で読んでみた
CIImageからCGImageに変換する
// CIImageだとSwiftUIのImageでは表示されない為、CGImageに変換
let context = CIContext()
guard let cgImage = context.createCGImage(scaledCiImage,
from: scaledCiImage.extent)
else { return nil }
return UIImage(cgImage: cgImage)
UIKitでUIImage(ciImage:)
を表示する場合だと表示されるのですが、SwiftUIのImage
でCIImage
から生成したUIImage
を表示させた場合には画面にQRコードが表示されませんでした。
こちらはCIImage
をCGImage
に変換して、UIImage(cgImage:)
に渡すことで解決出来ました。
実際に動かしてみる
QRコードを生成するモデルは出来たので、実際にViewと組み合わせて使ってみます。
ContentView
import SwiftUI
struct ContentView: View {
@State private var qrCodeImage: UIImage?
private let qrCodeGenerator = QRCodeGenerator()
var body: some View {
VStack(spacing: 16) {
if let qrCodeImage {
Image(uiImage: qrCodeImage)
.resizable()
.frame(width: 200, height: 200)
} else {
ReloadButton {
qrCodeImage = qrCodeGenerator.generate(with: "https://careers.classmethod.jp/")
}
}
Text("Hello, QRCode!")
}
.padding()
}
}
QRコードの画像がnil
ではない場合は、QRコード画像が表示され、nil
の場合はリロードボタンが表示されます。
ReloadButton
import SwiftUI
struct ReloadButton: View {
let action: () -> Void
var body: some View {
VStack {
Text("データを取得出来ませんでした")
Button {
action()
} label: {
Text("再取得")
}
}
}
}
デモ
画面表示時には、まだ特にQR生成処理を実行していないので、QRコードは表示されません。リロードボタンを押すと、QRコードが生成されます。
無事にQRコードの生成と表示は出来ました。次はせっかくなのでQRコードの中心にアイコンを設置してQRコードを可愛く仕上げましょう。
QRコードの中心に画像を設置する
UIGraphicsImageRenderer
を使用して、画像を重ねて一つのUIImage
を作成します。
import UIKit
extension UIImage {
// UIImageの中心に小さいUIImageを配置して合成する
func composited(withSmallCenterImage centerImage: UIImage) -> UIImage {
return UIGraphicsImageRenderer(size: self.size).image { context in
let imageWidth = context.format.bounds.width
let imageHeight = context.format.bounds.height
let centerImageLength = imageWidth < imageHeight ? imageWidth / 5 : imageHeight / 5
let centerImageRadius = centerImageLength * 0.2
// 背面に設置する親画像を描画
draw(in: CGRect(origin: CGPoint(x: 0, y: 0),
size: context.format.bounds.size))
// 中心に設置する画像のrectを設定
let centerImageRect = CGRect(x: (imageWidth - centerImageLength) / 2,
y: (imageHeight - centerImageLength) / 2,
width: centerImageLength,
height: centerImageLength)
// 画像に角丸をつける為のパスを作成
let roundedRectPath = UIBezierPath(roundedRect: centerImageRect,
cornerRadius: centerImageRadius)
// クリッピングパスを追加
roundedRectPath.addClip()
// 中心に設置する画像を描画
centerImage.draw(in: centerImageRect)
}
}
}
これで中心に可愛いアイコンがついたQRコードを作成できました!
今回は、正方形で角丸のアイコンのような画像が中心に配置されるようにしていますが、好みによって値は変更してみてください。
仕上げ
あとは、QRCodeGenerator
のgenerate(with:)
返り値の箇所に上記で作成したcomposited(withSmallCenterImage:)
を使用するだけです。
return UIImage(cgImage: cgImage).composited(withSmallCenterImage: UIImage(named: "icon_cute")!)
完成したもの
おわりに
これで一味違った可愛らしいQRコードを作成することが出来ました!ちょっとした手間でQRコードが可愛くなるのでQRコード作成の際は是非試してみてください。
完成したものはGitHubに載せているので気になる方は見てみてください。
またクラスメソッドでは一緒に働く仲間を募集中です。このQRコードを読み取ってその先に進んでみてください。
お待ちしています!
おわり