I have an example of using gestures to draw on pictures.
However, after zooming in on the picture, the position drawn by the gesture cannot correspond to the actual position on the screen.
Or is there any other way to reach the hand-painted layer and merge it into the picture?
Are there any similar examples or solutions?
struct ContentView: View {
@State private var lines: [[CGPoint]] = [[]]
@State private var currentLineWidth: CGFloat = 5.0
@State private var drawnImage: UIImage = .init()
@State private var zoomScale: CGFloat = 1.0
@State private var lastZoomScale: CGFloat = 1.0
@State private var imageOffset: CGSize = .zero
@State private var lastImageOffset: CGSize = .zero
@State private var drawing: Bool = true
var body: some View {
VStack {
ZStack {
Image(uiImage: drawnImage)
.resizable()
.background(Color.gray)
.aspectRatio(contentMode: .fit)
.scaleEffect(zoomScale)
.offset(x: imageOffset.width, y: imageOffset.height)
.gesture(
MagnificationGesture()
.onChanged { value in
if lastZoomScale * value >= 1 && lastZoomScale * value <= 2 {
zoomScale = lastZoomScale * value
}
}
.onEnded { value in
lastZoomScale = zoomScale
}
.simultaneously(with:
DragGesture()
.onChanged { value in
imageOffset = CGSize(width: lastImageOffset.width + value.translation.width, height: lastImageOffset.height + value.translation.height)
}
.onEnded { value in
lastImageOffset = imageOffset
}
)
)
if (drawing) {
Canvas { context, size in
for line in lines {
var path = Path()
guard let firstPoint = line.first else { continue }
path.move(to: adjustedPoint(firstPoint))
for point in line.dropFirst() {
path.addLine(to: point)
}
context.stroke(path, with: .color(.blue), lineWidth: currentLineWidth)
}
}
.background(Color.init(white: 1, opacity: 0.5))
.scaleEffect(zoomScale)
.offset(x: imageOffset.width, y: imageOffset.height)
.gesture(
DragGesture(minimumDistance: 0)
.onChanged { value in
let newPoint = CGPoint(
x: (value.location.x / zoomScale) - (imageOffset.width / zoomScale) ,
y: (value.location.y / zoomScale) - (imageOffset.height / zoomScale)
)
lines[lines.count - 1].append(newPoint)
}
.onEnded { value in
lines.append([])
}
)
}
}
.frame(maxWidth: self.drawnImage.size.width * zoomScale, maxHeight: self.drawnImage.size.height * zoomScale)
.border(Color.black, width: 5)
HStack {
Button(action: {
lines = [[]]
}) {
Text("Clear")
.padding()
.background(Color.red)
.foregroundColor(.white)
.cornerRadius(8)
}
Button(action: {
drawing.toggle()
if(!drawing){
print("Moving")
mergeDrawingWithImage(image: self.drawnImage)
}else{
print("Drawing")
}
}) {
if (drawing) {
Text("Drawing")
.padding()
.background(Color.orange)
.foregroundColor(.white)
.cornerRadius(8)
} else {
Text("Moving")
.padding()
.background(Color.orange)
.foregroundColor(.white)
.cornerRadius(8)
}
}
Button(action:{
mergeDrawingWithImage(image: self.drawnImage)
}) {
Text("Save Drawing")
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
Slider(value: $currentLineWidth, in: 1...10, step: 1) {
Text("Line Width")
}
.padding()
}
.padding()
}
.onAppear(perform: {
self.drawnImage = UIImage(named: "background")!
})
}
private func adjustedPoint(_ point: CGPoint) -> CGPoint {
return CGPoint(
x: point.x,
y: point.y
)
}
func mergeDrawingWithImage(image: UIImage) {
let renderer = UIGraphicsImageRenderer(size: image.size)
let mergedImage = renderer.image { context in
// Draw the original image
image.draw(at: CGPoint.zero)
// Draw the lines on top of the original image
let strokeColor = UIColor.blue
context.cgContext.setStrokeColor(strokeColor.cgColor)
context.cgContext.setLineWidth(currentLineWidth)
context.cgContext.setLineCap(.round)
for line in lines {
guard let firstPoint = line.first else { continue }
context.cgContext.beginPath()
context.cgContext.move(to: firstPoint)
for point in line.dropFirst() {
context.cgContext.addLine(to: point)
}
context.cgContext.strokePath()
}
}
self.drawnImage = mergedImage
clear()
}
func clear() {
lines = [[]]
}
}
Try to add scale parameter, but still incorrect.
New contributor
Josh Chen is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.