You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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("保存蒙版")
                }
                
            }
        }
    }
}

尝试过的无效方案

  1. 自定义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)
        }
    }
}
  1. 使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 23:30:45