Ios 在SwiftUI中更改UIHostingController的根视图

Ios 在SwiftUI中更改UIHostingController的根视图,ios,swift,swiftui,Ios,Swift,Swiftui,对于新的SwiftUIiOS应用程序,我在SceneDelegate if let windowScene = scene as? UIWindowScene { let window = UIWindow(windowScene: windowScene) if Auth().token == nil { window.rootViewController = UIHostingController(rootView: StartRegistrationView

对于新的
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以转到我的选项卡视图?我似乎无法使用
SwiftUI
找到任何解决方案


我是否应该改为使用
环境
对象并侦听对用户的
身份验证
状态的更改?

声明ApprotView,如下所示:

struct AppRootView: View {

    @ObservedObject private var auth: Auth
    var body: some View {
        Group {
            if auth.token != nil {
                MainTabbedView()
            } else {
                StartRegistrationView()
            }
        }
    }
}
然后在SceneDelegate中将其设置为根视图:

window.rootViewController=ui宿主控制器(rootView:approtView(auth:$auth))


您必须将视图绑定到Auth(),方法是像我上面所做的那样传入它,或者在您的环境中设置它。SwiftUI的美妙之处在于,只要令牌不是零,视图就会重新绘制,用户就会在MainTabbedView中找到自己。

非常好的答案LuLugaga,如果您不想使用@ObservableObject,则会更新,所以不会一直更新,您可以使用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)
    }
}
struct RootView:View{
var loginViewModel:loginViewModel=loginViewModel()
@State var tokenString=“”
var body:一些观点{
团体{
tokenString.count>0?任意视图(ContentView(模型:playerViewModel)):任意视图(LoginView(LoginView模型:LoginView模型))
}.onReceive(loginViewModel.tokenString){
self.tokenString=$0
}
}
}
类LoginViewModel{
让tokenString=PassthroughSubject()
变量标记:字符串{
获取{return”“}
设置{
self.tokenString.send(newValue)
}
}

通过使用此选项,我们可以通过实现上述代码在任何按钮单击中更改根视图。

对于更改根视图时的一些动画,请在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
    })

您可以创建一个路由器类

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

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

在较新的Xcode中,有一些SwiftUI模板更改,下面是如何加载初始视图。请参阅

因此,在本例中,第一个视图是
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
作为一些启动或动画。

关于以下方面的任何想法:属性类型“Auth”与其包装类型“ObservedObject”的“wrappedValue”属性的属性不匹配您的Auth类型是否实现了observeObject?Y因此,它必须是一个可观察对象。从今天起,将输出包装到
组中会生成编译器警告。相反,我们可以使用类型擦除包装器
AnyView
,例如
AnyView(MainTabbedView())
(无需
)惊人的方法,但是有没有一种方法可以使这种视图的更改具有动画效果?我自己无法做到。这部分起作用。在应用程序重新启动之前,新视图上的按钮根本没有响应。有没有办法解决此问题?当单击按钮更改rootview控制器时,新视图上的按钮变得没有响应。有没有办法要解决这个问题吗?反应迟钝与此方法无关。我相信这可能是由于按钮的zindex。请尝试更改按钮的索引。“.zindex(1)”
struct WelcomeView: View {
    
    @ObservedObject private var auth = Auth()
    
    var body: some View {
        if auth.token != nil {
            HomeView()
        } else {
            SignUpView(auth: auth)
        }
    }
}
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)
    }
}