How to perform an action after NavigationLink is tapped?

Yes, NavigationLink does not allow such simultaneous gestures (might be as designed, might be due to issue, whatever).

The behavior that you expect might be implemented as follows (of course if you need some chevron in the list item, you will need to add it manually)

struct TestSimultaneousGesture: View {
    @State var showPlusButton = false
    @State var currentTag: Int?
    var body: some View {

        NavigationView {
            List {
                ForEach(0 ..< 12) { item in
                    VStack {
                        HStack(alignment: .top) {
                            Text("List item")
                            NavigationLink(destination: Text("Details"), tag: item, selection: self.$currentTag) {
                                EmptyView()
                            }
                        }
                        .padding(EdgeInsets(top: 5, leading: 10, bottom: 5, trailing: 10))
                        .foregroundColor(.black)
                        Divider()
                    }
                    .simultaneousGesture(TapGesture().onEnded{
                        print("Got Tap")
                        self.currentTag = item
                        self.showPlusButton = false
                    })
                    .simultaneousGesture(LongPressGesture().onEnded{_ in
                        print("Got Long Press")
                        self.currentTag = item
                        self.showPlusButton = false
                    })
                    .onAppear(){
                        self.showPlusButton = true
                    }
                }
            }
        }
    }
}

I'm using the following code. I prefer it to just NavigationLink by itself because it lets me reuse my existing ButtonStyles.


struct NavigationButton<Destination: View, Label: View>: View {
    var action: () -> Void = { }
    var destination: () -> Destination
    var label: () -> Label

    @State private var isActive: Bool = false

    var body: some View {
        Button(action: {
            self.action()
            self.isActive.toggle()
        }) {
            self.label()
              .background(
                ScrollView { // Fixes a bug where the navigation bar may become hidden on the pushed view
                    NavigationLink(destination: LazyDestination { self.destination() },
                                                 isActive: self.$isActive) { EmptyView() }
                }
              )
        }
    }
}

// This view lets us avoid instantiating our Destination before it has been pushed.
struct LazyDestination<Destination: View>: View {
    var destination: () -> Destination
    var body: some View {
        self.destination()
    }
}

And to use it:

var body: some View {
  NavigationButton(
    action: { print("tapped!") },
    destination: { Text("Pushed View") },
    label: { Text("Tap me") }
  )
}

Another alternative I have tried. Not using simultaneousGesture, but an onDisappear modifier instead. Code is simple and It works. One downside is that those actions happen with a slight delay. Because first the destination view slides in and after this the actions are performed. This is why I still prefer @Asperi's answer where he added .simultaneousGesture(LongPressGesture) to my code.

ForEach(0 ..< 12) {item in
    NavigationLink(destination: TransactionsDetailsView()) {
        VStack {
            HStack(alignment: .top) {
                Text("List item")
            }
            .padding(EdgeInsets(top: 5, leading: 10, bottom: 5, trailing: 10))
            .foregroundColor(.black)
            Divider()
        }
    }
    .onDisappear(){
        self.showPlusButton = false
    }
    .onAppear(){
        self.showPlusButton = true
    }
}

Tags:

Swiftui