1

I have this structure in my SwiftUI application:

App structure

Main container holds a NavigationView with two NavigationLinks, V1 and V4. From there, I can go to View1, View2 and View3 which I can navigate through with V1, V2 and V3 NavigationLinks.

I can go to a ResultView with the proper NavigationLinks Result1, Result2, Result3.

I have an EnvironmentObject that holds all the bool flags for the isActive property of the NavigationLinks.

When I reach ResultView, I need to pop the views and go to V4, without being able to access V1, V2 and V3 until later.

The problem is that the following shenanigans happen (on iPhone 8, iOS 13.4.1):

  • If I set V1 = false then V4 = true, the NavigationView has a weird behavior where it pops to whatever was its previous view (View1, View2 or View3) then goes to View4, then goes back to MainContainer.
  • If I only set V1 = false, it pops correctly to Main container

I need to set V2, V3, Result1, Result2 and Result3 back to false, to ensure I can restart from V1 and then go to V2, etc. Also, one of the requirements of my app is that I can't absolutely go back to View1, View2 and View3 after going to ResultView.

I've thought of two possible solutions but each one has it caveats:

  1. Hide a NavigationLink for View 4 in ResultView: How could I prevent going back to View1, 2 or 3 when pressing Back from V4, and present Main container instead?
  2. Use a Timer or DispatchQueue.main.asyncAfter to set V4 = true after setting V1 = false: seems a hacky solution, but I've already tried this and it has the same effect as not using it, plus I don't want to see a transition back and forth (just forth).

Do you have any suggestion on why the NavigationLinks do not behave correctly, or perhaps a better navigation model?

I've already implemented a solution where I'm using a custom Navigation Stack, where I can push and pop each screen whenever I want, the problem is that I lose some of the transitions and I'd really like to achieve this by using the standard NavigationLinks.

I'm leaving an example here.

import SwiftUI

class NavigationCoordinator: ObservableObject {

    @Published var goToFirstScreen: Bool = false
    @Published var goToSecondScreen: Bool = false
    @Published var goToThirdScreen: Bool = false
    @Published var goToFourthScreen: Bool = false
}

struct ContentView: View {

    @EnvironmentObject var coordinator: NavigationCoordinator

    var body: some View {
        NavigationView {
            VStack {
                Spacer()
                NavigationLink(destination: FirstScreen(), isActive: self.$coordinator.goToFirstScreen) {
                    Text("Go To View1!")
                }
                Spacer()
                NavigationLink(destination: FourthScreen(), isActive: self.$coordinator.goToFourthScreen) {
                    Text("Go to View4!")
                }
                Spacer()
            }
        }
        .navigationViewStyle(StackNavigationViewStyle())
    }

}

struct FirstScreen: View {

    @EnvironmentObject var coordinator: NavigationCoordinator

    var body: some View {
        NavigationLink(destination: SecondScreen(), isActive: self.$coordinator.goToSecondScreen) {
            Text("Go to View2!")
        }
    }
}

struct SecondScreen: View {

    @EnvironmentObject var coordinator: NavigationCoordinator

    var body: some View {
        NavigationLink(destination: ThirdScreen(), isActive: self.$coordinator.goToThirdScreen) {
            Text("Go to View3!")
        }
    }

}

struct ThirdScreen: View {

    @EnvironmentObject var coordinator: NavigationCoordinator

    var body: some View {
        Button("Go to View4") {
            self.coordinator.goToFirstScreen = false
            self.coordinator.goToFourthScreen = true
        }
    }
}

struct FourthScreen: View {

    @EnvironmentObject var coordinator: NavigationCoordinator

    var body: some View {
        Button("Pop to root!") {
            self.coordinator.goToFourthScreen = false
        }
    }
}
riciloma
  • 1,456
  • 2
  • 16
  • 31
  • The issue is due to *I have an EnvironmentObject that holds all the bool flags for the isActive property of the NavigationLinks.* - this is not the way how navigation stack now works. Would you provide demo testable code with expectations, so it could fixed? – Asperi May 12 '20 at 11:20
  • Using an ObservableObject was the most suggested idea here on SO, do you have an example of the other way? About the code, I'm sorry but I can't share it due to NDA. I'll try later to build an example app to show what I mean. – riciloma May 12 '20 at 12:28
  • Updated with example. I want to go from Third Screen to reset stack with First Screen and Forth Screen. How can I do it? – riciloma May 13 '20 at 06:36

1 Answers1

0

Don't know if this is exclusively Xcode 12 feature or not, but I found a simple easy answer to this problem and answered it recently here: https://stackoverflow.com/a/63760934/13293344.

You just use navigationLink's tag and selection and inject an environmentobject to track the selection - set the environmentobject selection to nil to pop back to root view from any subsequent child view. also allows you to go to any navigationlink by setting the environmentobject selection to whatever tag the navigation link is.

Super Noob
  • 750
  • 8
  • 17