Changing from NavigationView to NavigationStack

Hi,

I am having trouble changing from deprecated NavigationView to NavigationStack.

I have looked through the article, and a number of examples and tutorials - they all show examples where the user clicks on a choice or a button in the view. But in my case, the navigation is programmatic based on changes to a value. I have below the old code, and my attempt at new revised code ( needs to run on IOS16, and cannot use API introduced in IOS 17). The new code compiles fine, but the screen does not navigate to new views as the value changes. I debug print the value as it changes, and the value for 'detailView' does change. The old code works without a problem - just that I get a warning that I am using deprecated APIs.

The old code:

struct MainView: View {
   @EnvironmentObject var gameVm: GameViewModel

   var body: some View {
      NavigationView {
         VStack {
            NavigationLink(destination: PlayView().transition(.slide),
               tag: .playView, selection: $gameVm.detailView) {
               EmptyView()
            }
            NavigationLink(destination: GamePausedView().transition(.scale),
                tag: .gamePausedView, selection: $gameVm.detailView) {
               EmptyView()
            }
            NavigationLink(destination: SettingsView().transition(.slide),
               tag: .settingsView, selection: $gameVm.detailView) {
               EmptyView()
            }
            IdleView().transition(.slide)
         }
      }
      .navigationViewStyle(.stack)
    }
}

My attempt at the revised code:

struct GameMainView: View {
   @EnvironmentObject var gameVm: GameViewModel
   @State private var path = NavigationPath()
   
   var body: some View {
      NavigationStack(path: $path) {
         VStack {
            NavigationLink(value: DetailView.playView) {
               EmptyView()
            }
            NavigationLink(value: DetailView.gamePausedView) {
               EmptyView()
            }
            NavigationLink(value: DetailView.settingsView) {
               EmptyView()
            }
            IdleView().transition(.slide)
         }
      }
      .navigationDestination(for: DetailView.self) { dtlViewType in
         if dtlViewType == .playView {
            PlayView().transition(.slide)
         } else if dtlViewType == .settingsView {
            SettingsView().transition(.slide)
         } else if dtlViewType == .gamePausedView {
            GamePausedView().transition(.slide)
         }
      }
   }
}

What am I doing wrong? Thanks in advance for any help.

Answered by DTS Engineer in 790791022

The navigationDestination(for:destination:) modifier need to be applied inside the NavigationStack. For example:

enum DetailView: Hashable {
    case playView
    case gamePausedView
    case settingsView
}


struct ContentView: View {
    var body: some View {
        NavigationStack {
            VStack {
                NavigationLink(value: DetailView.playView) {
                    Text("Play")
                }
                
                NavigationLink(value: DetailView.settingsView) {
                    Text("Settings")
                }
                
                NavigationLink(value: DetailView.gamePausedView) {
                    Text("Pause")
                }
            }
            .navigationDestination(for: DetailView.self) { selection in
                switch selection {
                case .gamePausedView:
                    Text("Game paused view")
                case .playView:
                    Text("Play view")
                case .settingsView:
                    Text("Settings view")
                }
            }
        }
    }
}

The navigationDestination(for:destination:) modifier need to be applied inside the NavigationStack. For example:

enum DetailView: Hashable {
    case playView
    case gamePausedView
    case settingsView
}


struct ContentView: View {
    var body: some View {
        NavigationStack {
            VStack {
                NavigationLink(value: DetailView.playView) {
                    Text("Play")
                }
                
                NavigationLink(value: DetailView.settingsView) {
                    Text("Settings")
                }
                
                NavigationLink(value: DetailView.gamePausedView) {
                    Text("Pause")
                }
            }
            .navigationDestination(for: DetailView.self) { selection in
                switch selection {
                case .gamePausedView:
                    Text("Game paused view")
                case .playView:
                    Text("Play view")
                case .settingsView:
                    Text("Settings view")
                }
            }
        }
    }
}

@DTS Engineer,

Thank you for the correction. I made the change, and there is still no programmatic navigation. Your answer also requires that the user click on the links you provide in order to navigate.

What I am looking for is the equivalent to my old code where the navigation is done programmatically. In that case, code elsewhere ( run at the start) determines the detailvVew and navigation is done automatically. Hence the EmptyView I have for the old style navigationLinks. How do I achieve that in the new style navigationDestination ? Thanks

Changing from NavigationView to NavigationStack
 
 
Q