Photos 框架


ImageRenderer

将视图转换为图像

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
        }

}

完整例子:使用 @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)
        }
    }
    
}

将视图渲染为 PDF

ImageRenderer 类还可以将任何 SwiftUI 视图渲染为 PDF,所有文本和形状仍然是矢量的,它们可以完美地缩放。(iOS 16 中的改进功能)

使用 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: 指定要渲染的内容
        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)")
        }
    }

}
  1. 访问共享的照片库:首先使用 PHPhotoLibrary.shared() 获取照片库的单例实例,这样可以与用户相册进行交互
  2. 执行保存performChanges 块是执行保存图片操作的地方。这里使用 PHAssetCreationRequest.creationRequestForAsset(from:) 来创建一个新的照片资产,使用要保存的 UIImage 对象作为参数
  3. 处理保存完成completionHandler 闭包在保存操作完成时被调用,它允许我们处理保存操作的结果,无论是成功还是失败。它提供了两个参数: success(一个布尔值,表示操作是否成功)和 error(如果操作失败,则为一个可选的 Error 对象)
  4. 处理出错信息:如果 success 参数为 false,则说明保存操作失败了。在这种情况下,使用 error 对象的 localizedDescription 属性打印出一条可读的错误信息