在SwiftUI中更改UIHostingController的根视图

Edw*_*ard 4 ios swiftui

对于新的SwiftUIiOS应用,我在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()
}
Run Code Online (Sandbox Code Playgroud)

当用户尚未注册或登录时,他们将进入注册流程。

用户注册后,如何将RootView切换到TabView?我似乎找不到使用的任何解决方案SwiftUI

我应该改为使用一个Environment对象并收听如何更改用户Auth状态?

小智 9

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()
}
Run Code Online (Sandbox Code Playgroud)

通过使用它,我们可以通过实现上述代码在任何按钮单击中更改 rootView。


ron*_*y_y 7

对此答案进行一点更新,您还可以通过执行以下操作来实现以下目的:

您可以创建一个Router

首先创建UIApplication的扩展

import Foundation
import UIKit

extension UIApplication {
    
    var keyWindow: UIWindow? {
        return UIApplication.shared.connectedScenes
            .filter { $0.activationState == .foregroundActive }
            .first(where: { $0 is UIWindowScene })
            .flatMap({ $0 as? UIWindowScene })?.windows
            .first(where: \.isKeyWindow)
    }

}
Run Code Online (Sandbox Code Playgroud)

拿到窗口钥匙

然后创建一个Router类并添加以下方法:

import Foundation
import SwiftUI

final class Router {

    //MARK: Main flow.
    public static func showMain(window: UIWindow? = nil) {
        Router.setRootView(view: MainView(), window: window)
    }

    //MARK: private
    private static func setRootView<T: View>(view: T, window: UIWindow? = nil) {
        if window != nil {
            window?.rootViewController = UIHostingController(rootView: view)
            UIView.transition(with: window!,
                              duration: 0.3,
                              options: .transitionCrossDissolve,
                              animations: nil,
                              completion: nil)
            return
        }else {
            UIApplication.shared.keyWindow?.rootViewController = UIHostingController(rootView: view)
            UIView.transition(with: UIApplication.shared.keyWindow!,
                              duration: 0.3,
                              options: .transitionCrossDissolve,
                              animations: nil,
                              completion: nil)
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

您现在可以从代码中的任何位置调用它,例如从 SceneDelegate :

Router.showMain(window: window)
Run Code Online (Sandbox Code Playgroud)

或者不经过窗户

Router.showMain() //if the window is nil it will go to the else statement and use UIApplication.keyWindow.
Run Code Online (Sandbox Code Playgroud)

并且过渡将是动画的。

较旧的答案:

import Foundation
import UIKit
import SwiftUI

class Router {
    
    class var window: UIWindow? {
        if let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
            if let sceneDelegate = scene.delegate as? SceneDelegate {
                let window = UIWindow(windowScene: scene)
                sceneDelegate.window = window
                window.makeKeyAndVisible()
                return window
            }
        }
        return nil
    }
    
    static func showMain() {
        window?.rootViewController = UIHostingController(rootView: ContentView())
    }
    
}
Run Code Online (Sandbox Code Playgroud)

用法 :

Router.showMain()
Run Code Online (Sandbox Code Playgroud)

这样您就可以在任何给定时间决定将哪个窗口作为根窗口。


LuL*_*aGa 6

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()
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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.

  • 关于以下内容的任何想法:属性类型“Auth”与其包装类型“ObservedObject”的“wrappedValue”属性不匹配 (2认同)