SwiftUI中ImageRenderer无法保存含绘图的图片至相册
问题:SwiftUI绘制内容无法与原图一同保存到相册
我实现了一个SwiftUI视图,允许用户在图片上绘制,并将图片与绘图内容一起保存到相册。但目前的问题是:能正常在图片上绘制,但保存到相册的只有原始图片,绘图内容未被包含。
原始实现代码
import SwiftUI struct DrawView2: View { var image: UIImage @State var points: [CGPoint] = [] var body: some View { Image(uiImage: image) .resizable() .scaledToFit() .gesture( DragGesture() .onChanged { value in self.addNewPoint(value) } ) .overlay () { DrawShape(points: points) .stroke(lineWidth: 50) // 设置线条宽度 .foregroundColor(.green) .clipped() } .clipShape(Rectangle()) .contentShape(Rectangle()) } private func addNewPoint(_ value: DragGesture.Value) { points.append(value.location) } } struct DrawShape: Shape { var points: [CGPoint] func path(in rect: CGRect) -> Path { var path = Path() guard let firstPoint = points.first else { return path } path.move(to: firstPoint) for pointIndex in 1..<points.count { path.addLine(to: points[pointIndex]) } return path } } struct DrawHelper: View { var image: UIImage var body: some View { GeometryReader { proxy in VStack { let drawView = DrawView2(image: image) .aspectRatio(contentMode: .fit) .scaledToFit() .frame(width: proxy.size.width, height: proxy.size.height / 2, alignment: .center) drawView .cornerRadius(UIConstants.standardCornerRadius) Button { let renderer = ImageRenderer(content: drawView) if let image = renderer.uiImage { UIImageWriteToSavedPhotosAlbum (image, nil, nil, nil) } } label: { Text("保存蒙版") } } } } }
尝试过的无效方案
- 自定义
snapshot方法替换ImageRenderer:
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) } } }
- 使用
Canvas视图重构绘制逻辑:
struct DrawView3: View { var image: UIImage @State var points: [CGPoint] = [] var body: some View { Canvas { context, size in context.draw(Image(uiImage: image).resizable(), in: CGRect(origin: .zero, size: size)) context.stroke( DrawShape(points: points).path(in: CGRect(origin: .zero, size: size)), with: .color(.green), lineWidth: 50 ) } .gesture( DragGesture() .onChanged { value in self.addNewPoint(value) } ) } private func addNewPoint(_ value: DragGesture.Value) { points.append(value.location) } }
问题原因
保存时使用的视图实例是重新初始化的,其内部的@State变量points为空数组——因为@State是视图内部状态,渲染新实例时不会继承原视图的绘制数据。
解决方案
核心思路:将绘制状态points从子视图提升到父视图持有,确保保存时能获取到当前的绘制数据。
1. 重构绘制视图,外部传入状态
struct DrawView2: View { var image: UIImage // 改为绑定状态,由父视图控制 @Binding var points: [CGPoint] var body: some View { Image(uiImage: image) .resizable() .scaledToFit() .gesture( DragGesture() .onChanged { value in points.append(value.location) } ) .overlay () { DrawShape(points: points) .stroke(lineWidth: 50) .foregroundColor(.green) .clipped() } .clipShape(Rectangle()) .contentShape(Rectangle()) } }
2. 修改父视图,持有绘制状态并传递
struct DrawHelper: View { var image: UIImage // 父视图存储绘制点 @State private var drawPoints: [CGPoint] = [] var body: some View { GeometryReader { proxy in VStack { DrawView2(image: image, points: $drawPoints) .aspectRatio(contentMode: .fit) .scaledToFit() .frame(width: proxy.size.width, height: proxy.size.height / 2, alignment: .center) .cornerRadius(UIConstants.standardCornerRadius) Button { // 用当前绘制点创建渲染视图 let renderView = DrawView2(image: image, points: .constant(drawPoints)) .aspectRatio(contentMode: .fit) .scaledToFit() .frame(width: proxy.size.width, height: proxy.size.height / 2, alignment: .center) let renderer = ImageRenderer(content: renderView) renderer.scale = UIScreen.main.scale // 提升渲染清晰度 if let image = renderer.uiImage { UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil) } } label: { Text("保存蒙版") } } } } }
关键修改说明
- 将子视图的
@State改为@Binding,让父视图统一管理绘制状态 - 保存时创建新的视图实例,传入当前的绘制点,确保渲染内容完整
- 添加
renderer.scale = UIScreen.main.scale避免保存的图片模糊
如果使用Canvas方案,同样只需将points改为@Binding,并由父视图传入即可。
内容的提问来源于stack exchange,提问作者Akhilesh Joshi
相关产品推荐
相关产品推荐

