I've this View with which I want to let user draw over an image, and then save the image + drawing as Image in Photos.
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) // here you put width of lines
.foregroundColor(.green)
.clipped()
}
.clipShape(Rectangle())
.contentShape(Rectangle())
}
private func addNewPoint(_ value: DragGesture.Value) {
// here you can make some calculations based on previous points
points.append(value.location)
}
}
struct DrawShape: Shape {
var points: [CGPoint]
// drawing is happening here
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("Save Mask")
}
}
}
}
}
What's happening is: I'm able to draw on the image correctly but it saves only original image. no DrawingShape included in the final saved image.
Screenshot from the Simulator: enter image description here
Saved Image from the Photos: enter image description here
Instead of ImageRenderer, I used following method to save the view as Image but it
extension View {
func snapshot() -> UIImage {
let controller = UIHostingController(rootView: self)
let view = controller.view
let targetSize = controller.view.intrinsicContentSize
//let targetSize = CGSize(width: 512, height: 384)
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)
}
}
}
I tried another approach of using Canvas View but found it had the same problem.
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)
}
.onEnded { value in
// here you perform what you need at the end
}
)
}
private func addNewPoint(_ value: DragGesture.Value) {
// here you can make some calculations based on previous points
points.append(value.location)
}
}