Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI: Cancel TapGesture on parent view

I have view hierarchy in SwiftUI like

ParentView { 
//other views

ChildView().highPriorityGesture(TapGesture().onEnded {
                        print("Tap!")
                    })
// other views 
}self.gesture(tap)

And I want to have parent view handle all taps on the screen in spite of cases when user taps onto ChildView. Now both closures executes. How to stop tap gesture events propagating up view hierarchy?

like image 932
Michał Ziobro Avatar asked Jan 28 '20 09:01

Michał Ziobro


2 Answers

Well, probably there is some specific in which exactly ChildView and ParentView, because as tested below (Xcode 11.2 / iOS 13.2) child view gesture just overrides parent view gesture.

Here is demo.. tapped in yellow area, then tapped in green area - no mix callbacks

enter image description here

Complete module code

import SwiftUI

struct TestGesturesPriority: View {
    var body: some View {
        VStack {
            Text("Hello, World!")
                .padding()
                .background(Color.yellow)
                .gesture(TapGesture().onEnded {
                    print(" -- child")
                })
        }
        .frame(width: 400, height: 400)
        .background(Color.green)
        .gesture(TapGesture().onEnded {
            print(">> parent")
        })
    }
}

Update: variant for List-Row

Yeees... List (Parent) - Row (Child) case appeared very challenging... please find below approach, it looks weird but tested & works

struct TestGesturesPriority: View {

    let parentGesture = TapGesture().onEnded { // just for convenience
        print(">> parent")
    }
    
    @GestureState private var captured = false
    var body: some View {
        List {
            Text("Hello, World!").padding()
                    .background(Color.yellow)
                    .allowsHitTesting(true)
                    .gesture(DragGesture(minimumDistance: 0) // mimic Tap
                        .updating($captured, body: { (value, state, transaction) in
                        state = true // mark captured (will be reset automatically)
                    })
                    .onEnded { value in
                        // like Tap, but can be ignored if delta 
                        // is large or out of view
                        print(" -- child")
                    }
                )
        }
        .gesture(parentGesture, including: captured ? .subviews : .gesture)
    }
}

To summarise - actually I think it is another List defect

backup

like image 199
Asperi Avatar answered Sep 29 '22 16:09

Asperi


There's a slightly cleaner way to solve the tap localisation issue in List as follows:

struct TestListGestures: View {

    var body: some View {
        List {
            Text("Hello, World!").padding()
                    .background(Color.yellow)

              .gesture(LongPressGesture(minimumDuration: 0.001) // resolve response time
                    .onEnded { value in
                        print(" -- child")
                    }
                )
        }
        .gesture(LongPressGesture(minimumDuration: 0.001).onEnded({ _ in
          print(" -- parent")
        }), including: .gesture)
    }
}
like image 35
Pranav Kasetti Avatar answered Sep 29 '22 17:09

Pranav Kasetti