Swift UI 导出 Canvas 内容

标签 swift swiftui

我有一个 Canvas ,用户可以在上面画东西。我想导出用户在我的 Canvas 上绘制的任何内容,我正在使用以下扩展从 View 中获取图像

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)
        }
    }
}

但不幸的是,此扩展适用于任何简单 View 上的按钮。每当我将它用于我的 Canvas 时,它都会给我一张空白图像,它似乎没有获取我 Canvas 的内容。

是否可以导出我的 Canvas 内容?

是不是因为我在用笔画 Canvas ?

这就是我使用 Canvas 的方式:

    @State private var currentLine = Line(color: .red)
    @State private var drawedLines = [Line]()
    @State private var selectedColor: Color = .red
    @State private var selectedSize = 1.0
    private var colors:[Color] = [.red, .green, .blue, .black, .orange, .yellow, .brown, .pink, .purple, .indigo, .cyan, .mint]

    var canvasPallete: some View {
        return Canvas { context, size in
            for line in drawedLines {
                var path = Path()
                path.addLines(line.points)
                context.stroke(path, with: .color(line.color), lineWidth: line.size)
            }

        }.gesture(DragGesture(minimumDistance: 0, coordinateSpace: .local)
            .onChanged({ value in
                let point = value.location
                currentLine.points.append(point)
                currentLine.color = selectedColor
                currentLine.size = selectedSize
                drawedLines.append(currentLine)
            })
            .onEnded({ value in
                currentLine = Line(color: selectedColor)
             })
        )
    }

    var body: some View {
        VStack {
            canvasPallete
                .frame(width: 300, height: 300)
            Divider()
            HStack(alignment: .bottom ) {
                VStack{
                    Button {
                        print("Will save draw image")
                        let image = canvasPallete.snapshot()
                        
                        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
                    } label: {
                        Text("Save")
                    }
                    .padding([.bottom], 25)

                    Button {
                        drawedLines.removeAll()
                    } label: {
                        Image(systemName: "trash.circle.fill")
                            .font(.system(size: 40))
                            .foregroundColor(.red)
                    }.frame(width: 70, height: 50, alignment: .center)
                }
                VStack(alignment: .leading) {
                    Text("Colors:")
                    ScrollView(.horizontal, showsIndicators: false) {
                        HStack(spacing: 4) {
                            ForEach(colors, id:\.self){color in
                                ColoredCircleButton(color: color, selected: color == selectedColor) {
                                    selectedColor = color
                                }
                            }

                        }
                        .padding(.all, 5)

                    }
                    Text("Size:")
                    Slider(value: $selectedSize, in: 0.2...8)
                        .padding([.leading,.trailing], 20)
                        .tint(selectedColor)
                }
                .padding([.bottom], 10)

            }


        }.frame(minWidth: 400, minHeight: 400)
    }
}


    struct ColoredCircleButton: View {
        let color: Color
        var selected = false
        let onTap: (() -> Void)
    
        var body: some View {
            Circle()
                .frame(width: 25, height: 25)
                .foregroundColor(color)
                .overlay(content: {
                    if selected {
                        Circle().stroke(.gray, lineWidth: 3)
                    }
                })
                .onTapGesture {
                    onTap()
                }
        }
    }

最佳答案

这很好用!

您遇到的问题是,在您的示例代码中,canvasPallete 在调用 snapshot() 时没有获得框架修饰符。所以它以不是您想要的大小呈现,可能是 (0,0)。

如果您更改快照行,它将起作用:

let image = canvasPallete
    .frame(width: 300, height: 300)
    .snapshot()

还有一点!在 iOS 16 中,有一个用于将 View 渲染到图像的新 API。因此,对于 iOS 16 及更高版本,您可以跳过 View 扩展并编写:

let renderer = ImageRenderer(content: canvasPallete)
if let image = renderer.uiImage {
    UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
}

关于Swift UI 导出 Canvas 内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73186594/

相关文章:

swift - 如何在预览中设置环境对象

如果操作未立即完成,则 SwiftUI View 在绑定(bind)到 @Published var 时没有动画

ios - @State var 未按预期在 LazyVGrid 中更新

swift - 为什么在 SwiftUI 中应用自定义 ButtonStyle 后我的箭头快捷键不起作用?

ios - 如何以编程方式将 UIImageView 添加到 UIStackView ( Storyboard)

arrays - Swift 从 JSON 创建 tableView

swift - MLDataTable 中的多个目标列 - Swift 中的 CreateML 框架

xcode - Textview 插入符号/光标在 ios8 底部被截断

ios - Swift iOS CVCalendar : Change the initial displayed month

swift - .sink 和 Subscribers.Sink 有什么区别?