ImageRenderer
类可以将任何 SwiftUI 视图层次结构渲染为图像,然后可以以其他方式保存、共享或重用该图像。(iOS 16 中的改进功能)
基本例子:
func generateImage(note: Note) -> UIImage? {
// 创建渲染器 & 配置渲染器 & 设置颜色格式以确保正确显示
let renderer = ImageRenderer(content: NoteShareImage1View(note: note))
renderer.scale = UIScreen.main.scale
renderer.colorMode = .nonLinear
// 获取生成图片
if let uiImage = renderer.uiImage {
return uiImage
} else {
return nil
}
}
ImageRenderer
,这意味着需要用 @MainActor
标记渲染代码renderer.scale
,图像将以 1 倍比例渲染,这在 2 倍或 3 倍分辨率的屏幕上看起来会很模糊ImageRenderer(content:)
初始值设定项中,但将它们分离到专用视图中会让代码更清晰UIGraphicsImageRenderer
不同,没有简单的方法可以直接从 ImageRenderer
读取 PNG 或 JPEG 数据;因此正如在代码中看到的,我们需要读取其结果的 UIImage
方法。这使得跨平台用户的代码更加复杂完整例子:使用 @MainActor
确保渲染代码可以安全调用,将视图渲染到它自己的结构中,然后使用 ShareLink
共享结果。
struct ContentView: View {
@State private var text = "Your text here"
@State private var renderedImage = Image(systemName: "photo")
// 获取设备的屏幕显示倍数,后面用于生成
@Environment(\\.displayScale) var displayScale
var body: some View {
VStack {
renderedImage
ShareLink(
"Export",
item: renderedImage,
preview: SharePreview(Text("Shared image"), image: renderedImage)
)
TextField("Enter some text", text: $text)
.textFieldStyle(.roundedBorder)
.padding()
}
// 当显示视图时以及每当 text 发生变化时,都会调用渲染方法
.onChange(of: text) { _ in
render()
}
.onAppear {
render()
}
}
// 执行渲染方法:
@MainActor func render() {
let renderer = ImageRenderer(content: RenderView(text: text))
// 使用设备的显示倍数去渲染生成图片
renderer.scale = displayScale
if let uiImage = renderer.uiImage {
renderedImage = Image(uiImage: uiImage)
}
}
}
ImageRenderer
类还可以将任何 SwiftUI 视图渲染为 PDF,所有文本和形状仍然是矢量的,它们可以完美地缩放。(iOS 16 中的改进功能)
使用 ImageRenderer
创建 PDF 需要八个步骤:
render()
方法来启动渲染代码CGContext
对象来处理 PDF 页面完成后,您将获得 PDF 的 URL地址,并且可以随意使用
这听起来工作量很大,下面是一个完整的示例,它使用 ShareLink
将视图呈现为要导出的 PDF,其中的注释与上面的说明相匹配:
@MainActor
struct ContentView: View {
var body: some View {
ShareLink("Export PDF", item: render())
}
func render() -> URL {
// 1: 指定要渲染的内容
let renderer = ImageRenderer(content:
Text("Hello, world!")
.font(.largeTitle)
.foregroundStyle(.white)
.padding()
.background(.blue)
.clipShape(Capsule())
)
// 2: 指定文件输出的路径
let url = URL.documentsDirectory.appending(path: "output.pdf")
// 3: 开始渲染
renderer.render { size, context in
// 4: Tell SwiftUI our PDF should be the same size as the views we're rendering
var box = CGRect(x: 0, y: 0, width: size.width, height: size.height)
// 5: Create the CGContext for our PDF pages
guard let pdf = CGContext(url as CFURL, mediaBox: &box, nil) else {
return
}
// 6: Start a new PDF page
pdf.beginPDFPage(nil)
// 7: Render the SwiftUI view data onto the page
context(pdf)
// 8: End the page and close the file
pdf.endPDFPage()
pdf.closePDF()
}
return url
}
}
private func saveImageToAlbum(_ image: UIImage) {
// 1.访问共享的照片库
PHPhotoLibrary.shared().performChanges {
// 2.在变更的块中执行保存操作
PHAssetCreationRequest.creationRequestForAsset(from: image)
} completionHandler: { success, error in
// 3.处理保存操作的完成情况(如果失败,打印出错误信息)
if !success, let error = error {
print("Error saving image to album: \\(error.localizedDescription)")
}
}
}
PHPhotoLibrary.shared()
获取照片库的单例实例,这样可以与用户相册进行交互performChanges
块是执行保存图片操作的地方。这里使用 PHAssetCreationRequest.creationRequestForAsset(from:)
来创建一个新的照片资产,使用要保存的 UIImage
对象作为参数completionHandler
闭包在保存操作完成时被调用,它允许我们处理保存操作的结果,无论是成功还是失败。它提供了两个参数: success
(一个布尔值,表示操作是否成功)和 error
(如果操作失败,则为一个可选的 Error
对象)success
参数为 false
,则说明保存操作失败了。在这种情况下,使用 error
对象的 localizedDescription
属性打印出一条可读的错误信息