How to perform an action after NavigationLink is tapped?

Solution 1:

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)

import SwiftUI

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
                    }
                }
            }
        }
    }
}

struct TestSimultaneousGesture_Previews: PreviewProvider {
    static var previews: some View {
        TestSimultaneousGesture()
    }
}

Solution 2:

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") }
  )
}