I have 2 independent set of data, one is used to draw a line(a Path
), and the other is used to put a small view somewhere on the line(Circle)
.
You can imagine it just like a Slider(in fact the UX should be something like that)
I wanted to add a Drag Gesture on the Red Circle View, so that the user can slide it any where between the line end points. But the way i've implemented or thought it to be isn't working at all or is pretty bad.
The know set of data is:
Start
of theLine
- CGPointEnd
of theLine
- CGPointPosition
of theCircle View
- CGPoint
For simplicity of the example, i've added some simple points namely Top
& Bottom
, but in reality, what i'm trying to achieve is, establish a perimeter on a Map Layer, by fetching the Lat/Long Coordinates, converting it to Screen Coordinates space and plotting a Path
(line) for those 2 points and thereafter giving the ability to the user to label the Perimeter and giving the freedom to drag it along the perimeter line as per the user's need. (One label per line, no complications on that).
That being said, Slider wasn't an option.
Sample Code:
struct TestView: View {
@State var position = CGPoint(x: 60, y: 60)
let top = CGPoint(x: 50, y: 50)
let bottom = CGPoint(x: 300, y: 300)
var body: some View {
ZStack {
Path { path in
path.move(to: self.top)
path.addLine(to: self.bottom)
}
.stroke(Color.red, lineWidth: 5)
Circle()
.foregroundColor(.red)
.frame(width: 20)
.position(self.position)
.gesture(
DragGesture(minimumDistance: 0, coordinateSpace: .global)
.onChanged { drag in
print(drag.location)
if
self.top.x <= drag.location.x,
self.bottom.x >= drag.location.x,
self.top.y <= drag.location.y,
self.bottom.y >= drag.location.y,
self.pointOnLine(point: drag.location)
{
self.position = drag.location
}
}
)
}
}
}
Helper Method to check if point is on line:
func pointOnLine(point: CGPoint) -> Bool {
let dxc = point.x - top.x
let dyc = point.y - top.y
let dxl = bottom.x - top.x
let dyl = bottom.y - top.y
let cross = dxc * dyl - dyc * dxl
return cross == 0
}
Any help appreciated. Thanks in advance.