Open
Description
Describe the bug
While working on the gestures, and testing difference approaches, including Preference Keys to report changes up the view tree. I've noticed that the actual view order (in HStack) is being flipped. Result can be see in the attached video
HStack {
Rectangle()
.fill(Color.purple)
.frame(width: 100, height: 100)
.onTapGesture() {
print("🟢 1st gesture")
}
.onTapGesture() {
print("🟢 2nd gesture, should not be called")
}
.onTapGesture() {
print("🟢 3rd gesture, should not be called")
}
.simultaneousGesture(
TapGesture().onEnded({ _ in
print("🟢 simultaneousGesture gesture")
})
)
.onLongPressGesture {
print("🟢 onLongPressGesture")
}
Text("Tap Parent")
}
.background(
Color.gray
.onTapGesture() {
print("🩶 Background gesture")
}
)
.onTapGesture() {
print("🔵 Parent gesture")
}
Used code to trigger the issue
private struct GesturePreference: Equatable {
let priority: UInt8
let mask: GestureMask
}
private struct GesturePreferenceKey: PreferenceKey {
static var defaultValue: [GesturePreference] = []
static func reduce(value: inout [GesturePreference], nextValue: () -> [GesturePreference]) {
value.append(contentsOf: nextValue())
}
}
private struct GesturePreferenceModifier<G: Gesture>: ViewModifier {
@Environment(\.isEnabled) var isEnabled
@State var shouldEnableGestures: Bool = false
let gesture: G
let priority: UInt8
let mask: GestureMask
init(
gesture: G,
priority: UInt8,
mask: GestureMask
) {
self.gesture = gesture
self.priority = priority
self.mask = mask
}
func body(content: Content) -> some View {
GestureView(
gesture: gesture,
isEnabled: isGestureEnabled,
content: content
)
// ISSUE HAPPENS WITH EITHER PREFERENCE ATTACHED
.preference(key: GesturePreferenceKey.self, value: [
GesturePreference(priority: priority, mask: mask)
])
.onPreferenceChange(GesturePreferenceKey.self) { preferences in
// Find the highest priority preference that matches the mask
let highestMatchingPreference = preferences
.filter { $0.mask.contains(mask) }
.max { $0.priority < $1.priority }
// Disable gestures if no matching preference is found or it's not the highest priority
let newValue = highestMatchingPreference?.priority == priority
if self.shouldEnableGestures != newValue {
self.shouldEnableGestures = newValue
print("🚀", preferences.count, self.shouldEnableGestures, String(describing: gesture.body.self))
}
}
}
}
with the modifier applied like this:
modifier(GesturePreferenceModifier(gesture: gesture.body, priority: 0, mask: mask))
To Reproduce
Steps to reproduce the behavior:
- Create a preference key
- Create HStack View with modifier applied to it and its subviews
- change state (i.e. on tap)
- See error
Expected behavior
Views remain, in expected order
Screenshots
Desktop (please complete the following information):
- OS: Ventura 13.4.1
- Browser safari
- Version of the 16.5.2
- Version of Tokamak Gesture Support branch