Reputation: 1762
In my iOS 14 SwiftUI app, when user is not logged in, I make him go through a few setup screens before presenting the main logged in screen. At the last setup screen, I use a NavigationLink to present the main logged in screen. How do I clear the entire navigation stack such that the main logged in screen becomes the root / first screen in the navigation stack?
Upvotes: 1
Views: 7195
Reputation: 764
Out of curiosity I started thinking about a solution to clear everything down to my root view and remembered that setting the .id() of a view will force a reload.
I'm posting this as a discussion point in first place not as an actual answer since I'm interested in your opinion if this is a legit approach.
Code is kept to the minimum to demonstrate the general idea and I haven't considered any memory leaks yet
import SwiftUI
// MARK: - SessionManager
class SessionManager: ObservableObject {
var isLoggedIn: Bool = false {
didSet {
rootId = UUID()
}
}
@Published
var rootId: UUID = UUID()
}
// MARK: - ContentView
struct ContentView: View {
@ObservedObject
private var sessionManager = SessionManager()
var body: some View {
NavigationView {
NavigationLink("ContentViewTwo", destination: ContentViewTwo().environmentObject(sessionManager))
}.id(sessionManager.rootId)
}
}
// MARK: - ContentViewTwo
struct ContentViewTwo: View {
@EnvironmentObject
var sessionManager: SessionManager
var body: some View {
NavigationLink("ContentViewTwo", destination: ContentViewThree().environmentObject(sessionManager))
}
}
// MARK: - ContentViewThree
struct ContentViewThree: View {
@EnvironmentObject
var sessionManager: SessionManager
var body: some View {
NavigationLink("ContentViewThree", destination: ContentViewFour().environmentObject(sessionManager))
}
}
// MARK: - ContentViewFour
struct ContentViewFour: View {
@EnvironmentObject
var sessionManager: SessionManager
var body: some View {
Button(action: {
sessionManager.isLoggedIn.toggle()
}, label: {
Text("logout")
})
}
}
Upvotes: 2
Reputation: 2234
Now that I am aware of .sheet()
and .fullscreenCover()
, I would prefer that answer, but being new to swiftUI this was my initial approach.
You could use a @State variable to control the behaviour. Create two navigation views, that are enclosed in a if condition based on your bool.
@State var showOnBoarding = true
if $showOnBoarding {
OnboardingNavigationView()
} else {
CoreNavigationView()
}
One with on-boarding links and the other containing your core navigation links. One for your onboarding and then one for logged in.
Each of your navigation links would also be initialized with the isActive Binding parameter.
struct OnboardingNavigationView: View {
var body: some View {
NavigationView {
NavigationLink("Step 1",destination: NextStepView(), isActive: $showOnBoarding)
//etc..
}
}
}
struct CoreNavigationView: View {
var body: some View {
NavigationView {
NavigationLink("Login Screen",destination: AccountView(), isActive: $showOnBoarding)
//etc..
}
}
}
Once the user logs in, you'd toggle that var. Each of your NavigationLinks would use the isActive property bound to showOnBoarding
. So once logged in they won't be able go back to the inactive onboarding screens and will be in your 'new' navigation stack where your login screen is the root screen.
See section 'Presenting a Destination View with Programmatic Activation' SwiftUI NavigationLink
Upvotes: -1
Reputation: 1256
.sheet()
or .fullScreenCover()
on root / first screen,self.presentationMode.wrappedValue.dismiss()
Upvotes: 1