Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to push a new root view using SwiftUI without NavigationLink?

Tags:

ios

swiftui

I have a login screen. After user fill up credential, I want to verify it then start a new root view so user won't be able to navigate back to the login view. I currently have

Button(action: { 
// launch new root view here 
}, label: {Text("Login")}).padding()

Majority of answers I found online are using navigation link which I don't want to. Some other answers suggest to utilize AppDelegate by UIApplication.shared.delegate which isn't working for me because I have SceneDelegate

like image 442
user1865027 Avatar asked Sep 02 '25 06:09

user1865027


2 Answers

Here is possible alternate approach of how to replace root view completely... using notifications

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    let loginRootViewNotification = 
         NSNotification.Name("loginRootViewNotification") // declare notification
    private var observer: Any?               // ... and observer


    ...
    // in place of window creation ...
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: contentView)

        observer = NotificationCenter.default.addObserver(forName: loginRootViewNotification, object: nil, queue: nil, using: { _ in
            let anotherRootView = AnotherRootView() 
            // create another view on notification and replace
            window.rootViewController = UIHostingController(rootView: anotherRootView)
        })

in your desired place post needed notification

Button(action: { 
// launch new root view here 
   NotificationCenter.default.post(loginRootViewNotification)
}, label: {Text("Login")}).padding()
like image 141
Asperi Avatar answered Sep 05 '25 01:09

Asperi


Another approach with Notifications is to place the observer inside your RootView and check for changes in a variable to decide which View should be presented, here is a very simplified example:

struct RootView: View {

    @State var isLoggedIn: Bool = false

    var body: some View {
        Group {
            VStack{
                if isLoggedIn {
                    Text("View B")
                    Button(action: {
                        NotificationCenter.default.post(name: NSNotification.Name("changeLogin"), object: nil)
                    }) {
                        Text("Logout")
                    }
                } else {
                    Text("View A")
                    Button(action: {
                        NotificationCenter.default.post(name: NSNotification.Name("changeLogin"), object: nil)
                    }) {
                        Text("Login")
                    }
                }
            }
        }.onAppear {
            NotificationCenter.default.addObserver(forName: NSNotification.Name("changeLogin"), object: nil, queue: .main) { (_) in
                self.isLoggedIn.toggle()
            }
        }
    }

And load RootView in your rootViewController as usual.

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    let contentView = RootView()
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: contentView)
        self.window = window
        window.makeKeyAndVisible()
    }
}
like image 31
zgluis Avatar answered Sep 05 '25 00:09

zgluis