I would like to be able to detect if there are any views that fall under the location of a DragGesture.
I have tried adding DragGesture to each view (circle in this case) hoping the action would transfer across views for a continuous DragGesture motion. But that didn't work.
Surely there is a way?

import SwiftUI
struct ContentView2: View {
var body: some View {
ZStack {
Image(.background)
.resizable()
.scaledToFill()
.ignoresSafeArea()
.gesture(
DragGesture(minimumDistance: 5)
.onChanged { value in
print ("DG detetected at \(value.location.x) : \(value.location.y)")
})
HStack {
Circle()
.fill(.red)
.frame(width: 100, height: 100)
.gesture(
DragGesture(minimumDistance: 0)
.onChanged { value in
print ("DG in red")
})
Circle()
.fill(.white)
.frame(width: 100, height: 100)
Circle()
.fill(.blue)
.frame(width: 100, height: 100)
}
}
}
}
#Preview {
ContentView2()
}
I would suggest saving the drag location in a GestureState variable. Then you can use a GeometryReader behind each of the circles to detect whether the drag location is inside the circle.
It is important that the drag gesture and the GeometryReader are referring to the same coordinate space.
A GestureState variable is automatically reset at the end of drag, so there is no need to reset it manually. In the example below, the only other variable that needs to be reset when drag ends is the text info.
struct ContentView: View {
@GestureState private var dragLocation = CGPoint.zero
@State private var dragInfo = " "
private func dragDetector(for name: String) -> some View {
GeometryReader { proxy in
let frame = proxy.frame(in: .global)
let isDragLocationInsideFrame = frame.contains(dragLocation)
let isDragLocationInsideCircle = isDragLocationInsideFrame &&
Circle().path(in: frame).contains(dragLocation)
Color.clear
.onChange(of: isDragLocationInsideCircle) { oldVal, newVal in
if dragLocation != .zero {
dragInfo = "\(newVal ? "entering" : "leaving") \(name)..."
}
}
}
}
var body: some View {
ZStack {
Color(white: 0.2)
VStack(spacing: 50) {
Text(dragInfo)
.foregroundStyle(.white)
HStack {
Circle()
.fill(.red)
.frame(width: 100, height: 100)
.background { dragDetector(for: "red") }
Circle()
.fill(.white)
.frame(width: 100, height: 100)
.background { dragDetector(for: "white") }
Circle()
.fill(.blue)
.frame(width: 100, height: 100)
.background { dragDetector(for: "blue") }
}
}
}
.gesture(
DragGesture(coordinateSpace: .global)
.updating($dragLocation) { val, state, trans in
state = val.location
}
.onEnded { val in
dragInfo = " "
}
)
}
}
For versions of iOS before iOS 17, you would need to use the version of .onChange that only takes 1 parameter (just omit the parameter oldVal).

I've solved it using the DragGesture on the ZStack and embedded it all in a GeometryReader to split the screen in different portions inside the gesture. The solution might be bit rough around the edges, of that I'm sure, but it is the closest I could get, I did my best. Here's the code:
GeometryReader { proxy in
let size = proxy.size
ZStack {
Color.black
.ignoresSafeArea()
HStack {
Circle()
.fill(.red)
.frame(width: 100, height: 100)
Circle()
.fill(.white)
.frame(width: 100, height: 100)
Circle()
.fill(.blue)
.frame(width: 100, height: 100)
}
} //: ZSTACK
.frame(width: size.width)
.gesture(
DragGesture(minimumDistance: 0)
.onChanged({ value in
let locationX = value.location.x
let locationY = value.location.y
/// Check if we are in the middle
if locationY >= (size.height / 2) - 50 && locationY <= (size.height / 2) + 50 {
let startRedCircle = locationX <= (size.width / 3 + 10)
let startWhiteCircle = locationX >= (size.width / 3 + 10) && locationX <= (2 * size.width / 3 - 10)
let startBlueCircle = locationX >= (2 * size.width / 3 - 10) && locationX <= (3 * size.width / 3 - 30)
if (startRedCircle) {
print("Red")
} else if (startWhiteCircle) {
print("White")
} else if(startBlueCircle) {
print("Blue")
}
}
})
)
} //: GEOMETRY
Of course you may need some adjustments based on your specific needs.
And here's the result: 
Let me know if it was of any help!
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