NavigationLink dismissal

I’m a bit lost with the NavLink dismiss ability. I’ve watched Chris’ early foundation course that explains the NavStack and Link. His examples show the linked view with a ‘back’ link in the top-left corner.

I’m hiding that with a modifier, but I still need a button to dismiss the linked view. I’ve discovered that there’s a limit to the back and forth between views without having them be dismissed properly.

I’ve not seen in any course videos where it talks about this.

Help appreciated!
Thank you!

1 Like

Hi Mathews!

If you are hiding this default back button that is provided by NavigationStack/NavigationLink, then it is really good to give a user a few options how to get back to the “main” view.
There are a few options there:

  1. Create a custom back button

I.e. in many cases people want custom wording there instead of having “Back” text there or they just want to have a “chevron.left” icon there. This could be one option.

  1. Swipe back to the main view

You get this natively, but remember when you hide the back button with the extension, you would most probably need to write another extension to bring back this functionality as hiding the “back button” in NavigationLink will disable this functionality.

  1. Other dismiss option

As a last option what you can do, is to introduce @Environment(\.dismiss) property and have a button in your view. The button’s action will then call this dimiss() property which will actually dismisses your current view and brings you back to the main one.

I hope this gives a some ideas on how to tackle this :slight_smile: Let me know if that makes sense or even if you need more guidance in this matter.

Peter, can you provide a code sample that demonstrates the swipe back from a NavigationLink because as far as I know (and some testing that I have just done) that is not available natively. You can swipe down a .sheet but you can’t swipe right to dismiss a view navigated to via a NavigationLink. I’ll stand corrected if you can provide me some code to show how that can be achieved.

Hi Chris!

Maybe my phrasing is not 100% correct here, so sorry for that in advance! What I meant by 2nd point is that when you tap on NavigationLink your “detail” view is being presented and you can get back to your “home”/“main” view by either taping “< Back” button (unless it is not hidden) or by swiping from left edge of screen to the right. That’s what I meant by having “dismiss” by default in that case. Now when I think about it, maybe calling it dismiss is not 100% accurate?

The moment you hide the back button I think the swipe functionality is not working anymore. That’s when you need to write an extension for it in order for it to work again.

I understood what you were getting at but I can’t get any kind of swipe back from the leading edge no matter what method I use as a NavigationLink be that NavigationLink(destination: ..... or NavigationLink(value: .....) That goes for iOS16 and iOS 17 so I am not sure how you have been able to get that to work.

I think it should be easy as that:

NavigationStack {
 NavigationLink {
  DetailView()
 } label: {
  Text("Go to detail")
 }
}

Well knock me over with a feather.

I had the NavigationLink inside a List and that does not work.

Haha, it happens! :slight_smile: Glad it is working for you now!

BTW, here’s the extension you’d need to write in case you decided to hide the back button for the swipe to be still working as in previous example:

extension UINavigationController: UIGestureRecognizerDelegate {
    override open func viewDidLoad() {
        super.viewDidLoad()
        interactivePopGestureRecognizer?.delegate = self
    }

    public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        return viewControllers.count > 1
    }
}

This gets even more weird. This code example does not work.

struct ContentView: View {

    @State private var words = ["Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf"]
    var body: some View {
        NavigationStack {
            List(words, id: \.self) { word in
                NavigationLink(value: word) {
                    Text(word)
                }
            }
            .navigationDestination(for: String.self) { word  in
                SecondView(word: word)
            }
        }
        .padding(.horizontal)
    }
}

#Preview {
    ContentView()
}

struct SecondView: View {
    let word: String

    var body: some View {
        VStack(spacing: 30){
            Text("Second View")
                .font(.largeTitle)
            Text("Passed in string: \(word)")
                .font(.headline)
        }
    }
}

You are right! However, remove this horizontal padding or just move it before navigationDestination and then it is working just fine :sweat_smile:. I don’t understand why this padding would block this whole functionality…

Actually, it doesn’t need to be necessary before it can be also after, but it can’t be applied directly to NavigationStack

OK, now this has to be a bug in iOS 17.

This works.

The only difference is that the horizontal padding has been commented out.

struct ContentView: View {

    @State private var words = ["Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf"]
    var body: some View {
        NavigationStack {
            List(words, id: \.self) { word in
                NavigationLink(value: word) {
                    Text(word)
                }
            }
            .navigationDestination(for: String.self) { word in
                SecondView(word: word)
            }
        }
//        .padding(.horizontal)
    }
}

#Preview {
    ContentView()
}

struct SecondView: View {
    let word: String

    var body: some View {
        VStack(spacing: 30){
            Text("Second View")
                .font(.largeTitle)
            Text("Passed in string: \(word)")
                .font(.headline)
        }
    }
}

If you are running it on Simulator, try to slow down the animations and you’ll see that when you go into SecondScreen the padding creates this “no mans land” around the NavigationStack and I think that’s why it is not working :smiley: As this is exactly the area you need to “swipe”/“tap” on in order to get back.

BTW, even the documentation itself is stating the swipe action there :slight_smile:

Just tried it on my real phone and it works fine with the padding in place although it is a bit sketchy. Stupid simulator.

1 Like

Great chat. Learned something new today so that’s always a good thing.

Agreed! Thanks a lot!

I hope author won’t mind doing a bit of reading in his own post, haha.

I hope we have not confused you Darren. Sorry mate.