SwiftUI ScrollView 的 onTapGesture 被子事件调用

Gre*_*sky 13 ios swift swiftui

我偶然发现了一个与 SwiftUI 相关的事件冒泡问题,ScrollView并且已经能够将其简化为一小段代码。请参阅以下内容:

struct ContentView: View {
  var body: some View {
    ScrollView() {
      Rectangle()
      .fill(Color.red)
      .frame(width: 200, height: 200)
      .onTapGesture {
        print("Rectangle onTapGesture")
      }
    }
    .onTapGesture {
      print("ScrollView onTapGesture")
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

在 Rectangle 外部敲击时,我在控制台中看到:

ScrollView onTapGesture

但是,当点击矩形时,我看到打印了两行:

ScrollView onTapGesture

Rectangle onTapGesture

ScrollView 似乎也在响应它的孩子的事件......这不应该发生,对吧?我能做些什么来阻止这种情况?

编辑:更疯狂的是,这两行并不总是以相同的顺序出现!我已经看到它们在重新启动应用程序时交换了代码,而没有对代码进行任何更改。

我的目标是在 ScrollView 上使用 onTapGesture 来捕捉“解除”点击,即没有被任何 ScrollView 的孩子捕捉/处理的点击。

非常感谢!

小智 5

这是一个简单的解决方案,使用内容GeometryReader容器:VStackScrollView

\n\n
struct ContentView: View {\n  var body: some View {\n    GeometryReader { contentView in\n      ScrollView {\n        VStack {\n          Rectangle()\n            .fill(Color.red)\n            .frame(width: 200, height: 200)\n            .onTapGesture {\n              print("Rectangle onTapGesture")\n          }\n        }\n        .frame(minWidth: contentView.size.width, minHeight: contentView.size.height, alignment: .top)\n        .contentShape(Rectangle())\n        .onTapGesture {\n          print("ScrollViewArea onTapGesture")\n        }\n      }\n    }\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

由于我们从\ 的属性中获取动态值,这为您提供了一个VStack始终与其父级大小完全相同的容器。ScrollViewGeometryReadersize

\n\n

请注意,alignment: .top此容器上的 是为了使其表现得像普通容器一样ScrollView,将滚动项目锚定到顶部。额外的好处是,如果您删除该alignment属性,您的滚动项目将从屏幕中间\xe2\x80\x94开始,这是我在偶然发现该解决方案之前发现不可能做到的事情。这在用户体验方面可能很有趣,因为较短的列表可以垂直居中。我离题了。

\n\n

最后要注意的是.contentShape用于使新的VStack空白空间可点击的修饰符,这解决了您的问题。

\n\n

这个想法取自这篇Hacking with Swift ScrollVieweffects using GeometryReader 文章,概述了如何将这个想法推向另一个层次,在滚动时转换元素。非常有趣的东西!

\n