Search code examples
swiftswiftuiswiftui-navigationlinkswiftui-navigationview

Start where you left off in SwiftUI with NavigationView


I'm trying to have a multi-view workflow in SwiftUI where you can start back where you left off, yet still have nice transitions of NavigationView.

As a basic example, I want it to have SignupView (where you enter user credentials) > PersonalInfoView (where you enter other personal info) > etc.

I initially went with a simple NavigationLink from one step to the next. But I also want it so that if you complete the SignupView and then quit the app, if you open it again, the user can be brought to PersonalInfoView directly. As in, you can start back where you left off.

So I thought of using conditional rendering:

if !userExists { 
  SignupView() 
} else if !hasPersonalInfo {
  PersonalInfoView()
} else {
  Text("Signup complete")
}

But now whenever I go from SignupView to PersonalInfoView I don't get the nice sliding transition that I would if I were using a NavigationLink. And if I add a NavigationLink to go to PersonalInfoView, it will slide into it (like a typical navigation) then slide out into the the other version of the view (the conditional rendering one).

So I guess my question is, what's the best way to handle conditional views all the while remaining in the whole NavigationView paradigm. Thanks

EDIT: I might not have explained my issue well enough: I still want to use NavigationView for everything (in order to have the transitions, back button, etc). I just want to be able to start at different points in the process but for every view following that to be part of the navigation stack.


Solution

  • You can explicitly apply transitions to the views:

    if !userExists { 
      SignupView()
        .transition(.slide)
    } else if !hasPersonalInfo {
      PersonalInfoView()
        .transition(.opacity)
    } else {
      Text("Signup complete")
        .transition(.slide)
    }
    

    Also, for some transitions you may need to set the userExists inside the withAnimation block:

    withAnimation {
        userExists = true
    }
    

    EDIT

    Here is an updated version (assuming that userExists and hasPersonalInfo are persisted (eg. stored in UserDefaults):

    struct ContentView: View {
        var userExists = true
        var hasPersonalInfo = false
    
        var body: some View {
            NavigationView {
                VStack {
                    if !userExists {
                        SignupView()
                    } else if !hasPersonalInfo {
                        PersonalInfoView()
                    } else {
                        Text("Signup complete")
                    }
                }
            }
        }
    }
    
    struct SignupView: View {
        @State private var isLinkActive = false
    
        var body: some View {
            VStack {
                Text("SignupView")
                Button("Go to PersonalInfoView") {
                    self.isLinkActive = true
                }
                .background(NavigationLink(destination: PersonalInfoView(), isActive: $isLinkActive) { EmptyView() }.hidden())
            }
        }
    }
    
    struct PersonalInfoView: View {
        @State private var isLinkActive = false
    
        var body: some View {
            VStack {
                Text("PersonalInfoView")
                Button("Go to MainView") {
                    self.isLinkActive = true
                }
                .background(NavigationLink(destination: Text("Signup complete"), isActive: $isLinkActive) { EmptyView() }.hidden())
            }
        }
    }