// // ContentView.swift // ORCode // // import SwiftUI import CoreImage.CIFilterBuiltins struct ContentView: View { var body: some View { Image(uiImage: generateQRCode(from: "123")).interpolation(.none) .resizable() .scaledToFit() .frame(width: 100, height: 100) .padding() } func generateQRCode(from string: String) -> UIImage { let context = CIContext() let filter = CIFilter.qrCodeGenerator() let data = Data(string.utf8) filter.setValue(data, forKey: "inputMessage") if let outputImage = filter.outputImage { if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) { return UIImage(cgImage: cgimg) } } return UIImage(systemName: "xmark.circle") ?? UIImage() } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
生成和缩放二维码
Core Image 可以让我们基于任何字符串输入生成一个二维码,而且过程极快。不过,这里有一个问题:图像的尺寸很小,只包含承载数据必要的像素。要让二维码更好用,需要借助 SwiftUI 的图像插值。因此,在这一步我们要让用户在表单里输入他们的名字和邮件地址,然后用这两条信息生成一个能标识他们的二维码,并且放大这个二维码。
二维码是一块由黑白像素构成的方块,可以通过手机和其他设备来扫描。Core Image 对此提供了专门的滤镜。如果你之前学习过如何使用 Core Image 滤镜,你会发现下面的过程很相似。
首先,我们需要用一个新的 import
来导入 Core Image 内置的滤镜:
import CoreImage.CIFilterBuiltins
其次,我们要用两个属性来分别存储一个激活的 Core Image 上下文和一个 Core Image 的二维码生成器滤镜的实例。
let context = CIContext()<br>let filter = CIFilter.qrCodeGenerator()
接下来是有趣的部分:制作二维码。如果你还记得,Core Image 滤镜要求我们使用 setValue(_:forKey:)
来设置输入数据,然后把输出的 CIImage
转成 CGImage
,再把 CGImage
转成 UIImage
。这里的步骤相似,除了以下三点:
- 我们的输入是字符串,但滤镜的输入是
Data
,所以我们需要做转换。 - 如果转换因为某种原因失败,我们会返回 SF Symbols 的 “xmark.circle” 图像。
- 如果该图像不能读取 —— 理论上这是有可能的,因为 SF Symbols 是基于字符串的 —— 那么我们将返回一个空的
UIImage
。
把下面这个方法添加到 结构体:
func generateQRCode(from string: String) -> UIImage { let data = Data(string.utf8) filter.setValue(data, forKey: "inputMessage") if let outputImage = filter.outputImage { if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) { return UIImage(cgImage: cgimg) } } return UIImage(systemName: "xmark.circle") ?? UIImage() }
在 SwiftUI 中,通过函数分离功能的做法很奏效,因为这意味着我们放进 body
属性的代码将会尽可能地简单。实际上,我们可以直接使用 generateQRCode(from:)
生成的 Image
,然后把它放大到一个合理的尺寸 .
把这个新 Image
视图直接添加到 视图上面
Image(uiImage: generateQRCode(from: "\(name)\n\(emailAddress)")) .resizable() .scaledToFit() .frame(width: 200, height: 200)
不过,近距离观察一下二维码 —— 你注意到它是模糊的吗?这是因为 Core Image 生成的是小图像,而 SwiftUI 在放大时尝试平滑像素。
像二维码这样的线条艺术,最好是禁用图像插值,把下面这个 modifier 添加到图像:
.interpolation(.none)
这样二维码就会以比较锐利的方式渲染,因为 SwiftUI 只会复制像素而不会混合像素。对于相机来说,它不关心二维码长啥样,但这样对用户来说比较好看。