SwiftUI:取消父视图上的 TapGesture

Posted

技术标签:

【中文标题】SwiftUI:取消父视图上的 TapGesture【英文标题】:SwiftUI: Cancel TapGesture on parent view 【发布时间】:2020-01-28 09:57:08 【问题描述】:

我在 SwiftUI 中有视图层次结构,例如

ParentView  
//other views

ChildView().highPriorityGesture(TapGesture().onEnded 
                        print("Tap!")
                    )
// other views 
self.gesture(tap)

并且我想让父视图处理屏幕上的所有点击,尽管用户点击了 ChildView。现在两个闭包都执行了。 如何停止点击手势事件向上传播视图层次结构?

【问题讨论】:

有点糊涂……那你为什么不想处理highPriorityGestureChildView呢? 如果点击在子视图矩形上方,我不想让父视图识别手势。父视图覆盖整个屏幕前列表和子视图只是此列表中的单元格。尽管有这个单个子视图单元格,但我想在整个屏幕上被点击识别。我考虑添加较高优先级的手势块较低优先级的手势,但它不能以这种方式工作。 【参考方案1】:

嗯,可能有一些具体的确切位置是 ChildViewParentView,因为如下所示(Xcode 11.2 / ios 13.2)子视图手势只是覆盖了父视图手势。

这里是演示......点击黄色区域,然后点击绿色区域 - 没有混合回调

完整的模块代码

import SwiftUI

struct TestGesturesPriority: View 
    var body: some View 
        VStack 
            Text("Hello, World!")
                .padding()
                .background(Color.yellow)
                .gesture(TapGesture().onEnded 
                    print(" -- child")
                )
        
        .frame(width: 400, height: 400)
        .background(Color.green)
        .gesture(TapGesture().onEnded 
            print(">> parent")
        )
    

更新:List-Row 的变体

Yeees... List (Parent) - Row (Child) 案例看起来非常具有挑战性...请找到下面的方法,它看起来很奇怪,但经过测试和工作

struct TestGesturesPriority: View 

    let parentGesture = TapGesture().onEnded  // just for convenience
        print(">> parent")
    

    @GestureState private var captured = false
    var body: some View 
        List 
            Text("Hello, World!").padding()
                    .background(Color.yellow)
                    .allowsHitTesting(true)
                    .gesture(DragGesture(minimumDistance: 0) // mimic Tap
                        .updating($captured, body:  (value, state, transaction) in
                        state = true // mark captured (will be reset automatically)
                    )
                    .onEnded  value in
                        // like Tap, but can be ignored if delta 
                        // is large or out of view
                        print(" -- child")
                    
                )
        
        .gesture(parentGesture, including: captured ? .subviews : .gesture)
    

总结一下——其实我认为是另一个List缺陷

【讨论】:

也许在 List (Parent) > Row (Child) 里面有什么不同? @MichałZiobro,参见更新部分 让孩子取消从遍历到父母的手势是很疯狂的困难 - 但你拯救了一天!【参考方案2】:

List 中有一种更简洁的方法来解决点击本地化问题,如下所示:

struct TestListGestures: View 

    var body: some View 
        List 
            Text("Hello, World!").padding()
                    .background(Color.yellow)

              .gesture(LongPressGesture(minimumDuration: 0.001) // resolve response time
                    .onEnded  value in
                        print(" -- child")
                    
                )
        
        .gesture(LongPressGesture(minimumDuration: 0.001).onEnded( _ in
          print(" -- parent")
        ), including: .gesture)
    

【讨论】:

以上是关于SwiftUI:取消父视图上的 TapGesture的主要内容,如果未能解决你的问题,请参考以下文章

如何将 SwiftUI 视图动态添加到父视图?

SwiftUI - 在导航堆栈中弹回不会取消分配视图

WatchOS SwiftUI 动画视图调整父级大小

如何在 SwiftUI 中随时从子视图作为父视图访问数据?

SwiftUI 绑定到父视图重新渲染子视图

SwiftUI PresentationButton 在 watchOS 上单次使用后停止运行