1

I'm trying to save the state of my NavigationPath for my NavigationStack in SwiftUI by pushing all the views to the stack at once. That way if the user taps the Back button, they will still see the views in order (like an onboarding flow). The views are added to the stack and the onAppear action is called for each.

My problem is that when the user taps the Back button, onAppear is not called again. It seems as if the Views are all still in focus. In a normal path operation, such as tapping the Four Button, the Four View appears firing its onAppear action, then tapping the Back button returns to Three View firing its onAppear function. So the issue only occurs when pushing multiple views to the stack.

After appending the views to the stack and navigating Back (removing from the stack), I'm expecting the views to fire onAppear again. This is currently not happening.

Maybe I'm making things more complicated than they need to be... so if anyone has any tips on recovering navigation path state I'd appreciate it.

enum Destination: String{
    case oneView
    case twoView
    case threeView
    case fourView
}

struct PageView: View {
    var title: String
    @Binding var path: [Destination]
    
    var body: some View {
        Text(title)
            .onAppear(perform: {
                print("\(title) appear")
            })
            .navigationBarBackButtonHidden()
            .toolbar(content: {
                ToolbarItem(placement: .topBarLeading) {
                    Button("Back") {
                        path.removeLast()
                    }
                }
                if title == "Three View" {
                    ToolbarItem(placement: .topBarTrailing) {
                        Button("Four View") {
                            path.append(.fourView)
                        }
                    }
                }
            })
    }
}

struct ContentView: View {
    @State private var path: [Destination] = []
    
    var body: some View {
        NavigationStack(path: $path) {
            List {
                Button {
                    path.append(contentsOf: [.oneView, .twoView, .threeView])
                } label: {
                    Text("Three View")
                }
            }
            .navigationDestination(for: Destination.self) { destination in
                switch destination {
                case .oneView:
                    PageView(title: "One View", path: $path)
                case .twoView:
                    PageView(title: "Two View", path: $path)
                case .threeView:
                    PageView(title: "Three View", path: $path)
                case .fourView:
                    PageView(title: "Four View", path: $path)
                }
            }
        }
    }
}
2
  • I've decided to use this alternative method of onboarding state management not involving a NavigationStack, though I do still hope to figure out this issue. youtube.com/watch?v=gm7Xct50CFo Commented Feb 10, 2024 at 3:52
  • your question and the code you provided don't match, however "I'm expecting the views to fire onAppear again." <-- this is a wrong expectation. The onAppear can be relied upon when a view is added to the stack. Which is not the case when you tap Back button. Also loading a bunch of views only to make Back button to work right sounds wrong. Consider having a coordinator which dispatches the next view, given the current. Or many other ways to handle this case, just not loading views just for the sake of possible navigation Commented Feb 10, 2024 at 8:34

0

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.