本篇原文(標題:Generating beautiful QR code views in iOS using Swift)刊登於作者 Medium,由 Prafulla Singh 所著,並授權翻譯及轉載。
QR Code 是一種認證技術,廣泛應用於行動科技領域中。iOS 內建支援創建帶有 Payload 的漂亮 QR Code 圖像。在本篇教學中,我們將會學習這個技巧。
QR code 的基本組件:
- Payload,多數是一個網址
- QR code 錯誤修正功能
- 可選公司圖片/Logo
- 可選顏色
首先,讓我們先構建結構,然後再實作基本和漂亮的 QR Code 視圖。
struct QRCodeDataSet {
let logo: UIImage?
let url: String
let backgroundColor: CIColor
let color: CIColor
let size: CGSize
init(logo: UIImage? = nil, url: String) {
self.logo = logo
self.url = url
self.backgroundColor = CIColor(red: 1,green: 1,blue: 1)
self.color = CIColor(red: 1,green: 0.46,blue: 0.46)
self.size = CGSize(width: 300, height: 300)
}
init(logo: UIImage? = nil, url: String, backgroundColor: CIColor, color: CIColor, size: CGSize) {
self.logo = logo
self.url = url
self.backgroundColor = backgroundColor
self.color = color
self.size = size
}
}
這就是基本的結構,使用者要按需要傳遞 payload 和其他可選物件。
建立一個基本的 QR Code
使用 ‘CIQRCodeGenerator’ 過濾器建立一個 CIFilter 物件,然後利用 inputMessage 和修正鍵 inputCorrectionLevel 設置 Payload。 (在這個範例中,我們使用了 inputCorrectionLevel H。)
private func createCIImage() -> CIImage? {
guard let filter = CIFilter(name: "CIQRCodeGenerator") else {
return nil
}
filter.setDefaults()
filter.setValue(url.data(using: String.Encoding.ascii), forKey: "inputMessage")
filter.setValue("H", forKey: "inputCorrectionLevel")
//https://www.qrcode.com/en/about/error_correction.html
return filter.outputImage
}
然後會得到回傳的 CIImage:
為 QR Code 添加顏色
假色 (False Color) 的過濾器是 ‘CIFalseColor’。讓我們從前面函式提取圖像,並設置為輸入圖像 (Input Image),如此設置顏色:
private func updateColor(image: CIImage) -> CIImage? {
guard let colorFilter = CIFilter(name: "CIFalseColor") else { return nil }
colorFilter.setValue(image, forKey: kCIInputImageKey)
colorFilter.setValue(color, forKey: "inputColor0")
colorFilter.setValue(backgroundColor, forKey: "inputColor1")
return colorFilter.outputImage
}
然後會得到回傳的 CIImage:
為 QR Code 添加 Logo
讓我們創建新的過濾器 ‘CISourceOverCompositing’,然後將 Logo 圖像轉換為最終圖像的中心,將 Logo 設置為主圖像,並把前面函式中提取的圖像(也就是 QR 圖像)設置為背景。
private func addLogo(image: CIImage, logo: UIImage) -> CIImage? {
guard let combinedFilter = CIFilter(name: "CISourceOverCompositing") else { return nil }
guard let logo = logo.cgImage else {
return image
}
let ciLogo = CIImage(cgImage: logo)
let centerTransform = CGAffineTransform(translationX: image.extent.midX - (ciLogo.extent.size.width / 2), y: image.extent.midY - (ciLogo.extent.size.height / 2))
combinedFilter.setValue(ciLogo.transformed(by: centerTransform), forKey: "inputImage")
combinedFilter.setValue(image, forKey: "inputBackgroundImage")
return combinedFilter.outputImage
}
然後會得到回傳的 CIImage:
完整程式碼
import SwiftUI
import Foundation
import UIKit
struct ContentView: View {
var body: some View {
ZStack {
QRView().frame(width: 300, height: 300, alignment: .center)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct QRView: UIViewRepresentable {
func updateUIView(_ uiView: UIImageView, context: Context) {
}
func makeUIView(context: Context) -> UIImageView {
return UIImageView.init(image: QRCodeDataSet(logo: UIImage.init(named: "red-hat")!, url: "https://google.com").getQRImage())
}
}
struct QRCodeDataSet {
let logo: UIImage?
let url: String
let backgroundColor: CIColor
let color: CIColor
let size: CGSize
init(logo: UIImage? = nil, url: String) {
self.logo = logo
self.url = url
self.backgroundColor = CIColor(red: 1,green: 1,blue: 1)
self.color = CIColor(red: 1,green: 0.46,blue: 0.46)
self.size = CGSize(width: 300, height: 300)
}
//rgb(255, , 118)
init(logo: UIImage? = nil, url: String, backgroundColor: CIColor, color: CIColor, size: CGSize) {
self.logo = logo
self.url = url
self.backgroundColor = backgroundColor
self.color = color
self.size = size
}
func getQRImage() -> UIImage? {
guard var image = createCIImage() else { return nil}
///scale to width:height
let scaleW = self.size.width/image.extent.size.width
let scaleH = self.size.height/image.extent.size.height
let transform = CGAffineTransform(scaleX: scaleW, y: scaleH)
image = image.transformed(by: transform)
/// add logo
if let logo = logo, let newImage = addLogo(image: image, logo: logo) {
image = newImage
}
/// update color
if let colorImgae = updateColor(image: image) {
image = colorImgae
}
return UIImage(ciImage: image)
}
private func updateColor(image: CIImage) -> CIImage? {
guard let colorFilter = CIFilter(name: "CIFalseColor") else { return nil }
colorFilter.setValue(image, forKey: kCIInputImageKey)
colorFilter.setValue(color, forKey: "inputColor0")
colorFilter.setValue(backgroundColor, forKey: "inputColor1")
return colorFilter.outputImage
}
private func addLogo(image: CIImage, logo: UIImage) -> CIImage? {
guard let combinedFilter = CIFilter(name: "CISourceOverCompositing") else { return nil }
guard let logo = logo.cgImage else {
return image
}
let ciLogo = CIImage(cgImage: logo)
let centerTransform = CGAffineTransform(translationX: image.extent.midX - (ciLogo.extent.size.width / 2), y: image.extent.midY - (ciLogo.extent.size.height / 2))
combinedFilter.setValue(ciLogo.transformed(by: centerTransform), forKey: "inputImage")
combinedFilter.setValue(image, forKey: "inputBackgroundImage")
return combinedFilter.outputImage
}
private func createCIImage() -> CIImage? {
guard let filter = CIFilter(name: "CIQRCodeGenerator") else {
return nil
}
filter.setDefaults()
filter.setValue(url.data(using: String.Encoding.ascii), forKey: "inputMessage")
filter.setValue("H", forKey: "inputCorrectionLevel")
//https://www.qrcode.com/en/about/error_correction.html
return filter.outputImage
}
}
請記得更改圖像名稱,來讓程式碼可以正常運作。
參考資料
本篇原文(標題:Generating beautiful QR code views in iOS using Swift)刊登於作者 Medium,由 Prafulla Singh 所著,並授權翻譯及轉載。
作者簡介:Prafulla Singh,Block.one 的 iOS 開發者
譯者簡介:Kelly Chan-AppCoda 編輯小姐。