在SwiftUI中更改UIHostingController的根视图

时间:2019-09-25 18:47:53

标签: ios swiftui

对于新的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状态的更改?

6 个答案:

答案 0 :(得分:9)

声明一个AppRootView,如下所示:

struct AppRootView: View {

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

,然后在SceneDelegate中将其设置为根视图:

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

您必须通过像上面那样传递视图或在您的环境中进行设置,将视图绑定到Auth()。 SwiftUI的优点在于,只要令牌不为零,视图就会重新绘制,您的用户会在MainTabbedView中找到它们。

答案 1 :(得分:8)

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。

答案 2 :(得分:1)

对于某些动画,当更改rootview时,请在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
    })

答案 3 :(得分:0)

很好的回答LuLugaga,如果不想使用@Observablebject,则进行更新,这样就不会一直保持更新,可以使用Subject,一旦更新令牌字符串,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)
}
}

答案 4 :(得分:0)

您可以创建一个路由器类

-5

用法:

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

有了这个,您可以在任何给定时间决定要使用哪个窗口作为根。

答案 5 :(得分:0)

在较新的 Xcode 中,有一些 SwiftUI 模板更改,以下是您的初始视图的加载方式。参考https://developer.apple.com/documentation/swiftui/app

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

所以在这种情况下,第一个视图是 WelcomeView 并且这个视图负责导航到正确的视图,它可能是登录,主页

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

Auth 是一个确认 ObservableObject 协议的类,并有一个名为 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 用作一些飞溅或动画。