SwiftUI - Navigation animations not working when multiple links are in same view

357 views Asked by At

In my project I have a root view which contains several NavigationLinks. The links work, however the animation only works on one of them. I've created a very simple example project to highlight the issue. So I have a content view like this:

struct ContentView: View {
    @StateObject var viewModel: ContentViewModel
    
    var body: some View {
        NavigationView {
            VStack {
                Text("Content view")
                
                Button {
                    viewModel.goToTapped(.details)
                } label: {
                    Text("To Details")
                }
                
                navigationLinks
            }
            .padding()
        }
    }
    
    private var navigationLinks: some View {
        HStack {
            NavigationLink(
                destination:  DetailsView(viewModel: viewModel)
                    .navigationBarHidden(true),
                tag: ContentViewModel.ViewState.details,
                selection: $viewModel.viewState
            ) { EmptyView() }
            
            NavigationLink(
                destination:  ContentView(viewModel: .init())
                    .navigationBarHidden(true),
                tag: ContentViewModel.ViewState.content,
                selection: $viewModel.viewState
            ) { EmptyView() }
            
            NavigationLink(
                destination:  PaymentView(viewModel: viewModel)
                    .navigationBarHidden(true),
                tag: ContentViewModel.ViewState.payment,
                selection: $viewModel.viewState
            ) { EmptyView() }
            
            NavigationLink(
                destination:  CardView(viewModel: viewModel)
                    .navigationBarHidden(true),
                tag: ContentViewModel.ViewState.card,
                selection: $viewModel.viewState
            ) { EmptyView() }
        }
    }
}

The associate viewModel:

class ContentViewModel: ObservableObject {
    enum ViewState {
        case content
        case details
        case payment
        case card
    }
    
    @Published var viewState: ViewState?
    
    func goToTapped(_ viewState: ViewState) {
        self.viewState = viewState
    }
}

DetailsView:

struct DetailsView: View {
    @ObservedObject var viewModel: ContentViewModel
    
    var body: some View {
        VStack {
            Text("Details View")
            
            Button {
                viewModel.goToTapped(.payment)
                
            } label: {
                Text("To Payment")
            }
            
            Button {
                viewModel.goToTapped(.content)
            } label: {
                Text("Back to Content")
            }
        }
    }
}

PaymentView:

struct PaymentView: View {
    @ObservedObject var viewModel: ContentViewModel
    
    var body: some View {
        NavigationView {
            VStack {
                Text("Payment View")
                
                Button {
                    viewModel.goToTapped(.card)
                } label: {
                    Text("To Card")
                }
                
                Button {
                    viewModel.goToTapped(.details)
                } label: {
                    Text("Back To Details")
                }
            }
        }  
    }
}

CardView:

struct CardView: View {
    @ObservedObject var viewModel: ContentViewModel
    
    var body: some View {
        Text("Card view")
        
        Button {
            viewModel.goToTapped(.payment)
        } label: {
            Text("Back To Payment")
        }
    }
}

And here is the result:

enter image description here

So it seems that when the NavigationLink is declared within the same view as the transition trigger, the animation works fine. However, once we have navigated to a new view, whilst the navigation works, the animation does not.

I tried moving the NavigationLinks into the individual views, but of course because we are listening to the same viewState publisher for all views, this does not work because as soon as the viewState is changes, the the navigation stack dismisses and we go back to the root view (ContentView).

How can declare multiple NavigationLinks like this within a root view without impacting on the navigation animation?

1

There are 1 answers

1
DevB1 On

UPDATE

So in iOS 16 we now have NavigationStack. This works fine when I replace NavigationView with NavigationStack - however my app needs to support iOS14+ so wondering what I can use for earlier versions...