ImageRenderer

将视图转换为图像

iOS 16 中的改进功能。

ImageRenderer 类能够将任何 SwiftUI 视图层次结构渲染为图像,然后可以以其他方式保存、共享或重用该图像。

使用方式

// 最简单的,所需的代码是这样的:
let renderer = ImageRenderer(content: Text("Hello, world!"))

if let uiImage = renderer.uiImage {
    // use the rendered image somehow
}

这里有四个关键点需要注意:

  1. 如果您没有另外指定,您的图像将以 1 倍比例渲染,这在 2 倍或 3 倍分辨率的屏幕上看起来会很模糊
  2. 您不能在 main actor 之外使用 ImageRenderer ,这意味着需要用 @MainActor 标记渲染代码
  3. 您可以将要渲染的 SwiftUI 视图直接放入 ImageRenderer(content:) 初始值设定项中,但将它们分离到专用视图中会让代码更清晰
  4. 与旧的 UIGraphicsImageRenderer 不同,没有简单的方法可以直接从 ImageRenderer 读取 PNG 或 JPEG 数据;因此正如在代码中看到的,我们需要读取其结果的 UIImage 方法。这使得跨平台用户的代码更加复杂

完整例子

第二个示例,它会自动使用设备的正确图像比例,使用 @MainActor 确保渲染代码可以安全调用,将视图渲染到它自己的结构中,然后让用户使用 ShareLink 共享结果:

// 将要用于生成图片的视图
struct RenderView: View {
    let text: String
    var body: some View {
        Text(text)
            .font(.largeTitle)
            .foregroundStyle(.white)
            .padding()
            .background(.blue)
            .clipShape(Capsule())
    }
}

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)
        }
    }
    
}

将视图渲染为 PDF

iOS 16 中的改进功能。

ImageRenderer 类可以将任何 SwiftUI 视图渲染为 PDF,是的:所有文本和形状仍然是矢量,因此它们可以完美地缩放。

使用方式

使用 ImageRenderer 创建 PDF 需要八个步骤:

  1. 决定要渲染哪些视图
  2. 创建一个 URL 对象让 SwiftUI 可以写入图像数据
  3. 在图像渲染器上调用 render() 方法来启动渲染代码
  4. 告诉 SwiftUI 您希望 PDF 有多大。这可能是固定大小,如 A4 或 US Letter,也可能是您正在渲染的视图层次结构的大小。
  5. 创建一个 CGContext 对象来处理 PDF 页面
  6. 开始新的一页
  7. 将 SwiftUI 视图渲染到该页面上
  8. 结束页面并关闭 PDF 文档

完成后,您将获得 PDF 的 URL地址,并且可以随意使用

这听起来工作量很大,下面是一个完整的示例,它使用 ShareLink 将视图呈现为要导出的 PDF,其中的注释与上面的说明相匹配:

@MainActor
struct ContentView: View {

    var body: some View {
        ShareLink("Export PDF", item: render())
    }

    func render() -> URL {
        // 1: Render Hello World with some modifiers
        let renderer = ImageRenderer(content:
            Text("Hello, world!")
                .font(.largeTitle)
                .foregroundStyle(.white)
                .padding()
                .background(.blue)
                .clipShape(Capsule())
        )

        // 2: Save it to our documents directory
        let url = URL.documentsDirectory.appending(path: "output.pdf")

        // 3: Start the rendering process
        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
    }
}