我有一个 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/