[英]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 中的Gesture
與UITapGestureRecognizer
之前的工作方式不同。 即默認情況下它同時識別手勢。 如果您只想優先捕獲某些手勢,則需要在手勢上使用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 文檔了解詳細信息。
這是一個使用GeometryReader
和VStack
容器來存儲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")
}
}
}
}
}
由於我們從GeometryReader
的size
屬性獲得的動態值,這為您提供了一個始終與其父ScrollView
完全相同大小的VStack
容器。
請注意,此容器上的alignment: .top
是為了使其表現得像普通的ScrollView
一樣,將滾動項錨定到頂部。 額外的好處是,如果您刪除alignment
屬性,您的滾動項將從屏幕中間開始——在偶然發現該解決方案之前,我發現這是不可能做到的。 這在用戶體驗方面可能很有趣,因為較短的列表垂直居中可能是有意義的。 我跑題了。
最后一點是.contentShape
修飾符用於使新的VStack
的空白空間可點擊,從而解決了您的問題。
這個想法來自這個Hacking with Swift ScrollView effects using GeometryReader 文章概述了如何將這個想法提升到另一個層次,在滾動時轉換元素。 非常有趣的東西!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.