ios 在SwiftUI中更改UIHostingController的根视图

wecizke3  于 2022-12-24  发布在  iOS
关注(0)|答案(6)|浏览(373)

对于新的SwiftUI iOS应用程序,我在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()
}

当用户尚未注册或登录时,他们将进入注册流程。
用户注册后,如何将RootView切换到我的TabView?我似乎找不到任何使用SwiftUI的解决方案。
我应该改为使用Environment对象并侦听用户Auth状态的更改吗?

ktca8awb

ktca8awb1#

声明一个AppRootView,如下所示:

struct AppRootView: View {

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

然后在SceneDelegate中将其设置为根视图:
第一个月
你必须将你的视图绑定到Auth(),要么像我上面那样传入它,要么在你的环境中设置它。SwiftUI的美妙之处在于,只要令牌不是nil,视图就会重新绘制,你的用户就会在MainTabbedView中找到自己。

7nbnzgx9

7nbnzgx92#

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()
}

通过使用这个,我们可以通过实现上面的代码来更改任何按钮单击中的rootView。

p1tboqfb

p1tboqfb3#

非常好的答案Lugaga,更新如果你不想使用@Observablebject,所以不会一直更新,你可以使用主题,只要你更新令牌字符串,RootView将更新.

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)
    }
}
zed5wv10

zed5wv104#

    • 对此答案稍作更新,您还可以通过以下方式实现以下目标:**

您可以创建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)
    }

}
    • 获取关键窗口**。

然后创建一个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)
        }
    }

}

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

Router.showMain(window: window)

或者不经过Windows

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

并且过渡将被动画化。

    • 以前的答案:**
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())
    }
    
}

用法:

Router.showMain()

有了它,您就可以决定在任何给定的时间将哪个窗口作为根窗口。

46qrfjad

46qrfjad5#

对于更改根视图时的某些动画,请使用sceneDelegate中的以下代码:

window.rootViewController = UIHostingController(rootView: HomeView())

    // A mask of options indicating how you want to perform the animations.
    let options: UIView.AnimationOptions = .transitionCrossDissolve

    // The duration of the transition animation, measured in seconds.
    let duration: TimeInterval = 0.3

    // Creates a transition animation.
    UIView.transition(with: window, duration: duration, options: options, animations: {}, completion:
    { completed in
        // maybe do something on completion here
    })
piztneat

piztneat6#

在较新的Xcode中,SwiftUI模板有一些变化,下面是初始视图的加载方式。

@main
struct AuthCheckApp: App {
    var body: some Scene {
        WindowGroup {
            WelcomeView()
        }
    }
}

在本例中,第一个视图是WelcomeView,此视图负责导航到正确的视图,可能是login、home

struct WelcomeView: View {
    
    @ObservedObject private var auth = Auth()
    
    var body: some View {
        if auth.token != nil {
            HomeView()
        } else {
            SignUpView(auth: auth)
        }
    }
}

Auth是一个符合ObservableObject协议的类,它有一个被发布的属性token,所以当这个token有一个值时,它会在上面的例子中加载HomeView,如果为nil,它会打开SignUpView

class Auth: ObservableObject {
    @Published var token: String?
}

struct SignUpView: View {
    
    let auth: Auth
    
    var body: some View {
        VStack {
            Text("Hello, please click below button to login")
                .padding()
            Button("Login") {
                print("Loogin Tapped")
                auth.token = "TOKEN"
            }
        }
    }
}

struct HomeView: View {
    var body: some View {
        Text("Welcome Parth!")
            .padding()
            .background(Color.red)
    }
}

如果你有API依赖性,需要等待令牌,那么你可以使用WelcomeScreen作为一些splash或动画,这种方法将很有帮助。

相关问题