我有一个全屏 SpriteKit 场景视图,它可以在其内部处理平移手势。 在它上面,我用 OnTapGesture 覆盖了本机 SwiftUI 视图,它必须对它们的触摸做出反应,但不应干扰平移手势,并将它们传递到下面。
.allowHitTesting(false)
完全消除了对触摸的任何反应,平移手势可以工作,但按钮处理程序不会。
我写了一个小例子来重现我的情况:
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()
}
那么,重申一下:如何制作透明(或 0.0001 不透明度)覆盖按钮来捕获单次触摸,但可以将平移(或拖动)手势传递到其下方的视图?
根据我的经验,最好单独定义手势,然后根据需要使用它们。这将为将它们与组合手势修饰符(如
.simultaneously
、.exclusively
和 .sequenced
)以及 GestureMask
参数一起使用提供更大的灵活性。
这是修改后的代码供您尝试:
import SwiftUI
struct DragTapTestView: View {
//Gestures
let dragGesture = DragGesture(minimumDistance: 3)
.onChanged { value in
//simulates spriteKit drag handling,
//for example animated page flipping
print("DragGesture",value.velocity)
}
let flipLeftTap = TapGesture()
.onEnded { _ in
print("flip page left")
}
let flipRightTap = TapGesture()
.onEnded { _ in
print("flip page right")
}
//Body
var body: some View {
ZStack {
//Background with drag gesture
Rectangle() //simulates spriteKit view
.fill(.blue)
.ignoresSafeArea()
.gesture(dragGesture)
//Buttons
HStack(spacing: 0) {
//Flip Left
Color.clear
.border(.red, width: 2)
.contentShape(Rectangle())
.gesture(
flipLeftTap
.simultaneously(with: dragGesture) //Note this could trigger both a drag and a tap, depending on drag distance and timing
)
//Spacer
Color.clear
.border(.green, width: 2)
VStack(spacing: 0) {
//Flip Right
Color.clear
.border(.yellow, width: 2)
.contentShape(Rectangle())
.gesture(
dragGesture
.exclusively(before: flipRightTap) //more reliable - compare this with the left tap approach
)
//Flip Right - Alternate method using a button
Button {
print("flip right button PRESSED")
} label: {
Color.clear
.border(.orange, width: 2)
}
.highPriorityGesture(dragGesture, including: .gesture) //Button already has a native tap gesture
}
}
}
}
}
#Preview {
DragTapTestView()
}
请注意,向右翻转有两种方法 - 一种使用
Button
,一种不使用。
更重要的是,测试时要注意左右方法的区别。尝试多次拖动左侧区域,您可能会注意到有时它会同时触发拖动和点击。这可能会导致双页翻转,具体取决于您的实现。
将左侧区域的拖动与任何右侧区域的拖动进行比较,后者不应同时触发拖动和点击。