25

I have a new iOS 16 SwiftUI NavigationStack with navigation determined by the NavigationDestination modifier which works fine.

My question is why doesn't it animate smoothly by sliding back to the root view when clearing the NavigationPath if you are more than one view deep within the stack?

It works if you are only one level deep, but anything lower than that causes "popping to root" to just jump back to the root view without the sliding animation.

Is this a "feature" or bug or am I doing something incorrectly?

Steps to re-create the issue

  • Run the sample code below.
  • Click the first navigation link and then click "Pop To Root View" - notice that it "slides smoothly" back to root view.
  • Click the first or second link - then click the "Navigate to View 3" which shows view 3.
  • Then click "Pop to Root" and you'll notice that it jumps back to the root view rather than slides. That's my question - should it jump back or slide back?

Demo of Issue

enter image description here

Demo Code (using Xcode 14.0 and iOS 16.0):

import SwiftUI
struct DemoPop: View {

    @State private var path = NavigationPath()
    
    var body: some View {
        
        VStack {
            
            NavigationStack(path: $path) {
                   
                List {
                    Section("List One") {
                        NavigationLink("Navigate to View 1", value: "View 1")
                        NavigationLink("Navigate to View 2", value: "View 2")
                    }
                }
                .navigationDestination(for: String.self) { textDesc in
                    
                    VStack {
                        Text(textDesc).padding()
                        Button("Navigate to View 3") {
                            path.append("View 3")
                        }.padding()
                        
                        Button("Pop to Root View") {
                            path.removeLast(path.count)
                        }.padding()
                    }
                }
                .navigationTitle("Test Pop To Root")
            }
        }
    }
}
    

struct DemoPop_Previews: PreviewProvider {
    static var previews: some View {
        DemoPop()
    }
}

Update 1:

Think the code above is correct so possibly a bug as mentioned in comments as I have just seen a YouTube video that exhibits the same behaviour - Youtube tutorial - around time line 19:25 - you will see pop to root just jumps back to start.

Update 2:

This has been fixed in iOS 16.2

4
  • Seeing the same behavior on my end with my own test code. One level deep pop to root animated perfectly. Anything more and it just jumps back with no animation. My initial thought is that it's got to be a bug, but you would think that this was tested by a number of people before release, so perhaps it's something we're doing wrong.
    – kittonian
    Sep 28, 2022 at 18:27
  • I think this issue must be a feature as I have just seen a YouTube video that exhibits the same functionality - youtu.be/pwP3_OX2G9A - around time line 19:25 - you will see pop to root just jumps back to start.
    – Mgwd
    Sep 30, 2022 at 18:12
  • 4
    Definitely not a feature. I've already filed a bug report with Apple.
    – kittonian
    Sep 30, 2022 at 21:29
  • @kittonian Good, I hope it is a bug as it doesn't feel right just jumping back. Thanks for your comments.
    – Mgwd
    Oct 1, 2022 at 8:53

2 Answers 2

6

This has been fixed by Apple on iOS 16.2

For iOS 16.0 and 16.1 here's a 100% working, one-liner solution. Add this SPM library to your codebase https://github.com/davdroman/swiftui-navigation-transitions (version 0.7.1 or later) and simply:

import NavigationTransitions
import SwiftUI

struct DemoPop: View {

    @State private var path = NavigationPath()

    var body: some View {
        NavigationStack(path: $path) {
            List {
                Section("List One") {
                    NavigationLink("Navigate to View 1", value: "View 1")
                    NavigationLink("Navigate to View 2", value: "View 2")
                }
            }
            .navigationDestination(for: String.self) { textDesc in

                VStack {
                    Text(textDesc).padding()
                    Button("Navigate to View 3") {
                        path.append("View 3")
                    }.padding()

                    Button("Pop to Root View") {
                        path.removeLast(path.count)
                    }.padding()
                }
            }
            .navigationTitle("Test Pop To Root")
        }
        .navigationTransition(.default) // one-liner ✨
    }
}


struct DemoPop_Previews: PreviewProvider {
    static var previews: some View {
        DemoPop()
    }
}
1
  • Thanks, I can confirm as you say that ios16.2 has resolved this issue.
    – Mgwd
    Dec 17, 2022 at 11:47
5

Most likely a bug, please file a feedback w/ Apple.

That said, if you're on iOS, you can work around it with the following hack:

import UIKit

let animation = CATransition()
animation.isRemovedOnCompletion = true
animation.type = .moveIn
animation.subtype = .fromLeft
animation.duration = 0.3
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)

UIApplication.shared.keyWindow!.layer.add(animation, forKey: nil)
self.navigationPath.removeLast()
2
  • That will animate the whole window including toolbars, tabbars, etc.
    – Martin
    Nov 5, 2022 at 12:52
  • 1
    Indeed. Without tricks (injecting special views into the hierarchy), it's very hard to find the corresponding UIKIt view for a given SwiftUI view ­– so that's a compromise here until Apple hopefully fixes this bug. Nov 6, 2022 at 9:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.