I have a fulscreen SpriteKit scene view, that handles pan gestures within itself.
Over it, I have overlaid native SwiftUI views with OnTapGesture, which must react to them being touched but should not interfere with pan gestures, and pass them below.
.allowHitTesting(false)
completely removes any reaction to touches, pan gestures work but button handlers won’t.
I’ve written a small example that recreates my situation:
import SwiftUI
struct TestView: View
{
var body: some View
{
ZStack
{
Rectangle() //simulates spriteKit view
.fill(.blue)
.ignoresSafeArea()
.simultaneousGesture(DragGesture().onChanged(
{ value in
//simulates spriteKit drag handling,
//for example animated page flipping
print("DragGesture",value.velocity)
} ))
HStack(spacing: 0) //my buttons
{
Rectangle()
.fill(.red.opacity(0.00001))
.border(.red, width: 2)
.onTapGesture
{
// this one gets called,
// but sk drag gesture is NOT called
print("flip page left")
}
Rectangle()
.fill(.clear)
.border(.green, width: 2)
.onTapGesture
{
// this one is NOT called,
// and sk gesture is called
print("flip page right")
}
Button(action:
{
// this one gets called,
// but sk drag gesture is NOT called
print("button PRESSED")
},
label:
{
Rectangle()
.fill(.black)
.border(.yellow, width: 2)
})
}
}
}
}
#Preview {
TestView()
}
So, to reiterate: How can I make TRANSPARENT (or 0.0001 opacity) overlaying buttons that capture single touches but can pass pan (or drag) gestures to the views below them?
2
Answers
In my experience, it’s best to define the gestures separately and then use them as needed. This will offer more flexibility for using them with composing gestures modifiers like
.simultaneously
,.exclusively
and.sequenced
, but also withGestureMask
parameters.Here’s the revised code for you to try:
Note that there are two methods for flipping right – one using a
Button
, one without.More importantly, pay attention to the difference between the left and right methods as you test. Try dragging on the left area multiple times and you may notice that sometimes it can trigger both a drag and a tap at the same time. This may result in a double page flip, depending on your implementation.
Compare the dragging on the left area with the dragging on any of the right areas, which should not trigger both a drag and a tap.
If the drag gesture doesn’t have to be attached to the blue rectangle in the background, then it works to move the drag gesture to the
HStack
containing the buttons. Also, if you want the middle button to react to tap gestures too, you can add a.contentShape
modifier to it.In the updated example below, all buttons (including the clear middle button) react to taps and the whole area is sensitive to drag gestures. The rectangles that were filled with solid color have been replaced with just a plain color (because it’s simpler).