Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change the root view of UIHostingController in SwiftUI

Tags:

ios

swift

swiftui

For a new SwiftUI iOS app, I do the following in the SceneDelegate

if let windowScene = scene as? UIWindowScene {
    let window = UIWindow(windowScene: windowScene)
    if Auth().token == nil {
        window.rootViewController = UIHostingController(rootView: StartRegistrationView())
    } else {
        window.rootViewController = UIHostingController(rootView: MainTabbedView())
    }
    self.window = window
    window.makeKeyAndVisible()
}

When a user hasn't signed up or logged in they are taken to the registration flow.

Once a user has signed up, how can I switch the RootView to go to my TabView? I can't seem to find any solution using SwiftUI.

Should I instead use an Environment object and listen for changes to the User's Auth Status?

like image 841
Edward Avatar asked Sep 25 '19 18:09

Edward


People also ask

How do I dismiss to root view in SwiftUI?

wrappedValue. dismiss() from that child view will pop to the root view.

What is Uihostingcontroller SwiftUI?

A UIKit view controller that manages a SwiftUI view hierarchy.

What is uihostingcontroller in SwiftUI?

A UIKit view controller that manages a SwiftUI view hierarchy. Create a UIHostingController object when you want to integrate SwiftUI views into a UIKit view hierarchy.

How to use SwiftUI with UIKit?

You can use SwiftUI with UIKit very easily with the help of UIHostingController. Since UIKit does not speak SwiftUI, let’s introduce a mediator here, UIHostingController! UIHostingController creates a bridge between UIKit and SwiftUI framework. It works as a Container View for SwiftUI view, meaning you can use it as normal UIViewController.

How to have Swift UI view inside viewcontroller in SwiftUI?

Wrapping UIView or ViewController into SwiftUI is made easy with UIViewControllerRepresentable and UIViewRepresentable. Likewise, it is also possible to have SwiftUI View inside ViewController, by using UIHostingController. The official tutorial is here.

Is it possible to assign ibsegueaction to uitabbarcontroller as SwiftUI?

When you want to use SwiftUI in Storyboard IBSegueAction should suffice for most cases, but a subclass of UIHostingController also got its merit. The problem I found which I can't figure out a way to accomplish with IBSegueAction is when I try to assign one on UITabBarController 's root view controllers as SwiftUI.


3 Answers

Declare an AppRootView, something like this:

struct AppRootView: View {

    @ObservedObject private var auth: Auth
    var body: some View {
        Group {
            if auth.token != nil {
                MainTabbedView()
            } else {
                StartRegistrationView()
            }
        }
    }
}

and then in SceneDelegate set it as the root view:

window.rootViewController = UIHostingController(rootView: AppRootView(auth: $auth))

You have to bind your view to your Auth() either by passing it in as I did above or by setting it on your environment. The beauty of SwiftUI is that as soon as the token is not nil, the view will redraw and your user will find them selves in MainTabbedView.

like image 136
LuLuGaGa Avatar answered Oct 18 '22 00:10

LuLuGaGa


let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene

if let windowScenedelegate = scene?.delegate as? SceneDelegate {
   let window = UIWindow(windowScene: scene!)
   window.rootViewController = UIHostingController(rootView:ContentView())
   windowScenedelegate.window = window
   window.makeKeyAndVisible()
}

By using this we can change the rootView in any button click by implementing the above code.

like image 38
Sai Amara Avatar answered Oct 18 '22 00:10

Sai Amara


Very good answer LuLugaga, updated if you don't want to use @Observablebject so will not keep updating all the time, you can use Subject, as soon as you update token String, RootView will update.

struct RootView: View {

    var loginViewModel: LoginViewModel = LoginViewModel()

    @State var tokenString = ""

    var body: some View {
        Group {
            tokenString.count > 0 ? AnyView(ContentView(model: playerViewModel)) :  AnyView(LoginView(loginViewModel: loginViewModel))
        }.onReceive(loginViewModel.tokenString) {
            self.tokenString = $0
        }
    }
}


class LoginViewModel {

    let tokenString = PassthroughSubject<String, Never>()

    var token: String {
        get { return "" }
    set {
        self.tokenString.send(newValue)
    }
}
like image 4
zdravko zdravkin Avatar answered Oct 18 '22 00:10

zdravko zdravkin