方法1: 使用 drawHierarchy
extension View {
func snapshot() -> UIImage {
let controller = UIHostingController(rootView: self)
let view = controller.view
let targetSize = controller.view.intrinsicContentSize
view?.bounds = CGRect(origin: .zero, size: targetSize)
view?.backgroundColor = .clear
let renderer = UIGraphicsImageRenderer(size: targetSize)
return renderer.image { _ in
view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
}
}
}
注意:这个方法需要swiftUI view 设置 .edgesIgnoringSafeArea(.all)
完整demo:
struct ContentView: View {
var textView: some View {
VStack{
Text("ABCSF1")
Text("ABCSF2")
Text("ABCSF3")
Text("Hello, SwiftUI")
.padding()
.background(.blue)
.foregroundStyle(.white)
.clipShape(Capsule())
}
.edgesIgnoringSafeArea(.all)
}
var body: some View {
VStack {
textView
Button("Save to image") {
let image = textView.snapshot()
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
}
}
.edgesIgnoringSafeArea(.all)
}
}
extension View {
func snapshot() -> UIImage {
let controller = UIHostingController(rootView: self)
let view = controller.view
let targetSize = controller.view.intrinsicContentSize
view?.bounds = CGRect(origin: .zero, size: targetSize)
view?.backgroundColor = .clear
let renderer = UIGraphicsImageRenderer(size: targetSize)
return renderer.image { _ in
view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
}
}
}
方法2: 使用ImageRenderer,需要iOS16以上
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()
}
.onChange(of: text) { _ in render() }
.onAppear { render() }
}
@MainActor
func render() {
let renderer = ImageRenderer(content: RenderView(text: text))
// make sure and use the correct display scale for this device
renderer.scale = displayScale
if let uiImage = renderer.uiImage {
renderedImage = Image(uiImage: uiImage)
}
}
}
参考文章: