So I've got the following code:
import SwiftUI
struct ContentView : View {
@State private var draggingLocation = CGPoint.zero
@State private var startLocation = CGPoint.zero
@State private var dragging = false
var body: some View {
let GR = DragGesture(minimumDistance: 10, coordinateSpace: .global)
.onEnded { value in
self.dragging = false
self.draggingLocation = CGPoint.zero
self.startLocation = CGPoint.zero
}
.onChanged { value in
if !self.dragging {
self.dragging = true
}
if self.startLocation == CGPoint.zero {
self.startLocation = value.startLocation
}
self.draggingLocation = value.location
}
return ZStack {
if self.dragging {
Path { path in
path.move(to: CGPoint(x: self.startLocation.x-5, y: self.startLocation.y-5))
path.addLine(to: CGPoint(x: self.draggingLocation.x-5, y: self.draggingLocation.y+5))
path.addLine(to: CGPoint(x: self.draggingLocation.x+5, y: self.draggingLocation.y-5))
path.addLine(to: CGPoint(x: self.startLocation.x+5, y: self.startLocation.y+5))
}
.fill(Color.black)
}
Circle()
.fill(self.dragging ? Color.blue : Color.red)
.frame(width: 100, height: 100)
.gesture(GR)
.offset(
x: 75,
y: 75
)
Circle()
.fill(self.dragging ? Color.blue : Color.red)
.frame(width: 100, height: 100)
.gesture(GR)
.offset(
x: -75,
y: -75
)
}
.frame(width: 400, height: 400)
.background(Color.gray)
}
}
#if DEBUG
struct ContentView_Previews : PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif
Which results in this behavior:
I'd like to be able to drag the edge out from one circle and into the other, the problem of course is that the coordinate space of the Path
is relative to the gray box (ContentView
) and not global. A Path
has a property coordinateSpace
in the documentation but there's very little information how to use it, and googling the term with SwiftUI literally returns three results, all of which are really just links to Apple's currently sparse docs. Anyone have an idea on how to best approach this?
The data structure CGPoint represents a point in a two-dimensional coordinate system. The data structure CGRect represents the location and dimensions of a rectangle. The data structure CGSize represents the dimensions of width and height.
SwiftUI enables custom drawing with two subtly different types: paths and shapes. A path is a series of drawing instructions such as “start here, draw a line to here, then add a circle there”, all using absolute coordinates.
Coordinate spaces come in three flavours: .local, .global and .named. The first two are obvious. The third, named coordinate spaces, are extremely useful in cases like yours. They are also useful in combination with GeometryReader. For more details on that, check https://swiftui-lab.com/geometryreader-to-the-rescue/
Named coordinate spaces let you express a coordinate of one view, in the coordinate space of another. For that, SwiftUI let you specify a name for a view's coordinate space. Then, in other places of your code, you can make a reference of it. In your example, naming the coordinate of your ZStack is the way to go.
Here's the refactored code:
Note: I moved the Path below, only so that it draws in front of the circles. And also, pay attention to the first Path, which is only there to prevent what I think is a bug in ZStack.
import SwiftUI
struct ContentView : View {
@State private var draggingLocation = CGPoint.zero
@State private var startLocation = CGPoint.zero
@State private var dragging = false
var body: some View {
let GR = DragGesture(minimumDistance: 10, coordinateSpace: .named("myCoordinateSpace"))
.onEnded { value in
self.dragging = false
self.draggingLocation = CGPoint.zero
self.startLocation = CGPoint.zero
}
.onChanged { value in
if !self.dragging {
self.dragging = true
}
if self.startLocation == CGPoint.zero {
self.startLocation = value.startLocation
}
self.draggingLocation = value.location
}
return ZStack(alignment: .topLeading) {
Circle()
.fill(self.dragging ? Color.blue : Color.red)
.frame(width: 100, height: 100)
.overlay(Text("Circle 1"))
.gesture(GR)
.offset(x: 75, y: 75)
Circle()
.fill(self.dragging ? Color.blue : Color.red)
.frame(width: 100, height: 100)
.overlay(Text("Circle 2"))
.gesture(GR)
.offset(x: 200, y: 200)
if self.dragging {
Path { path in
path.move(to: CGPoint(x: self.startLocation.x-5, y: self.startLocation.y-5))
path.addLine(to: CGPoint(x: self.draggingLocation.x-5, y: self.draggingLocation.y+5))
path.addLine(to: CGPoint(x: self.draggingLocation.x+5, y: self.draggingLocation.y-5))
path.addLine(to: CGPoint(x: self.startLocation.x+5, y: self.startLocation.y+5))
}
.fill(Color.black)
}
}
.coordinateSpace(name: "myCoordinateSpace")
.frame(width: 400, height: 400, alignment: .topLeading)
.background(Color.gray)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With