簡體   English   中英

SwiftUI ScrollView 的 onTapGesture 被子事件調用

[英]SwiftUI ScrollView's onTapGesture getting called for child events

我偶然發現了一個與 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")
    }
  }
}

在矩形之外點擊時,我在控制台中看到:

ScrollView onTapGesture

但是,當點擊 Rectangle 時,我看到打印了兩行:

ScrollView onTapGesture

Rectangle onTapGesture

ScrollView 似乎也在響應其子事件......這不應該發生,對吧? 我能做些什么來阻止這種情況?

編輯:只是為了增加瘋狂,這兩行並不總是以相同的順序出現。 我已經看到它們在重新啟動應用程序時交換了代碼,而沒有對代碼進行任何更改。

我的目標是在 ScrollView 上使用 onTapGesture 來捕捉“關閉”的點擊,即沒有被任何 ScrollView 的孩子捕捉/處理的點擊。

非常感謝!

SwiftUI 中的GestureUITapGestureRecognizer之前的工作方式不同。 即默認情況下它同時識別手勢。 如果您只想優先捕獲某些手勢,則需要在手勢上使用ExclusiveGesture視圖修飾符,而不是使用默認的onTapGesture

TapGesture錯誤地檢測到觸摸,因為它反應太快(它的最小持續時間很短,大約 0.0001 秒)。 我通過用更合理的minimumDuration替換TapGesture輕松解決了這個問題:

LongPressGesture(minimumDuration: 0.001)

請注意,您可以根據需要減少或增加最短持續時間,但這是我測試時最穩定的。

順便說一句,這可能是一個SwiftUI 錯誤,我鼓勵您提交一份概述該問題的錯誤報告。

這是代碼:

struct ContentView: View {

  var tap: some Gesture {
    LongPressGesture(minimumDuration: 0.001)
      .exclusively(before: scrollTap)
      .onEnded { _ in
        print("Rectangle onTapGesture")
    }
  }

  var scrollTap: some Gesture {
    LongPressGesture(minimumDuration: 0.001)
      .onEnded { _ in
        print("ScrollView onTapGesture")
    }
  }

  var body: some View {
    ScrollView() {
      Rectangle()
      .fill(Color.red)
      .frame(width: 200, height: 200)
      .gesture(tap)
    }.gesture(scrollTap, including: .gesture)

  }
}

如果您有多個要專門忽略的視圖,請多次使用exclusively視圖修飾符(鏈接),或者如果您想反轉捕獲sequenced ,則使用順序。 您可以查看Gesture 文檔了解詳細信息。

這是一個使用GeometryReaderVStack容器來存儲ScrollView內容的簡單解決方案:

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

由於我們從GeometryReadersize屬性獲得的動態值,這為您提供了一個始終與其父ScrollView完全相同大小的VStack容器。

請注意,此容器上的alignment: .top是為了使其表現得像普通的ScrollView一樣,將滾動項錨定到頂部。 額外的好處是,如果您刪除alignment屬性,您的滾動項將從屏幕中間開始——在偶然發現該解決方案之前,我發現這是不可能做到的。 這在用戶體驗方面可能很有趣,因為較短的列表垂直居中可能是有意義的。 我跑題了。

最后一點是.contentShape修飾符用於使新的VStack的空白空間可點擊,從而解決了您的問題。

這個想法來自這個Hacking with Swift ScrollView effects using GeometryReader 文章概述了如何將這個想法提升到另一個層次,在滾動時轉換元素。 非常有趣的東西!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM