SwiftUI 将一个 ScrollView 中的 content 转为 image

将一个 ScrollView 中的 content 转为 image

在 iOS 16 / macOS 13 之前,将任何一个 View 转为 image 需要借助 NSHostingControllerUIHostingController 编写相关代码实现,而随着 Apple 在 iOS 16.0 / macOS 13 后发布了新的 API — ImageRenderer 后,仅需一行代码就能将一个 View 转为 image。

下面我们将借助ImageRenderer类将一个 ScrollView 中的 content 转为 image,即使 content 的高度高于屏幕高度,这种方法仍然可将全部的 content 转为 image。

建立一个 ScrollView

假设我们有下面一个 ScrollView:

import SwiftUI

struct ContentView: View {
    let fruits = ["Apple", "Banana", "Cherry"]
    
    var body: some View {
        VStack {
            Text("Fruits")
                .font(.largeTitle)
            
            ScrollView {
								VStack {
		                ForEach(fruits, id: \.self) { fruit in
		                    Text(fruit)
		                }
								}
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

获取 ScrollView 中 content 的 size

在这一步中,我们将 ScrollView 中 content 提取为一个子 View

import SwiftUI

struct ContentView: View {
    let fruits = ["Apple", "Banana", "Cherry"]
    
    var body: some View {
        VStack {
            Text("Fruits")
                .font(.largeTitle)
            
            ScrollView {
                InsideContentView(fruits: fruits)
                    .background(
                        ZStack {
                            GeometryReader {proxy in
                                Color.clear
                                    .onAppear {
                                        print(proxy.size)
                                    }
                            }
                        }
                    )
            }
        }
    }
}

struct InsideContentView: View {
    var fruits: [String]
    var body: some View {
        VStack {
            ForEach(fruits, id: \.self) { fruit in
                Text(fruit)
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

将 content 转为 image

import SwiftUI

struct ContentView: View {
    let fruits = ["Apple", "Banana", "Cherry"]
    @State var isTakeSnapshot: Bool = false
    @State var snapshot_proxy: [GeometryProxy] = []
    
    var body: some View {
        VStack {
            Text("Fruits")
                .font(.largeTitle)
            
            ScrollView {
                InsideContentView(fruits: fruits)
                    .background(
                        ZStack {
                            GeometryReader {proxy in
                                Color.clear
                                    .onChange(of: isTakeSnapshot) { _ in
                                        snapshot_proxy = []
                                        snapshot_proxy.append(proxy)
                                    }
                            }
                        }
                    )
            }
            Button("take snapshot", action: {
                isTakeSnapshot.toggle()
                let snapshot_size = snapshot_proxy[0].size
                let content = InsideContentView(fruits: fruits).frame(width: snapshot_size.width)
                let renderer = ImageRenderer(content: content)
                let iamge = renderer.nsImage ?? NSImage()
								// 在这里使用 image, 保存、显示或分享等
            })
        }
    }
}

struct InsideContentView: View {
    var fruits: [String]
    var body: some View {
        VStack {
            ForEach(fruits, id: \.self) { fruit in
                Text(fruit)
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Button 中 action 的代码就是我们的实现部分。

有以下几点需要注意的地方:

  • GeometryProxy 不支持初始化,所以我们用一个 GeometryProxy 数组的 State 变量用来存放 content 的 proxy。
  • .frame(width: snapshot_size.width) 保证截图宽度与 content 宽度一致,macOS 上不加 frame 修饰时,截图宽度可能会与 app 窗体宽度不一致。
  • 如果发现截图的分辨率较低,可以加上renderer.scale = 2.0 ,表示将截图分辨率提升 2 倍,详情参加官方文档。

你可能感兴趣的:(SwiftUI,笔记,swiftui,ios)