Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preference Key subviews flipped #540

Open
shial4 opened this issue Aug 12, 2023 · 0 comments
Open

Preference Key subviews flipped #540

shial4 opened this issue Aug 12, 2023 · 0 comments
Assignees
Labels
bug Something isn't working

Comments

@shial4
Copy link

shial4 commented Aug 12, 2023

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:

  1. Create a preference key
  2. Create HStack View with modifier applied to it and its subviews
  3. change state (i.e. on tap)
  4. See error

Expected behavior
Views remain, in expected order

Screenshots

Desktop (please complete the following information):

Screen_Recording_2023-08-08_at_18.14.15.mov
@shial4 shial4 added the bug Something isn't working label Aug 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Development

No branches or pull requests

2 participants