为啥可观察对象中的更新变量不会更新视图?

Posted

技术标签:

【中文标题】为啥可观察对象中的更新变量不会更新视图?【英文标题】:Why update variable in observable object does not update the view?为什么可观察对象中的更新变量不会更新视图? 【发布时间】:2019-11-05 19:51:03 【问题描述】:

我是 SwiftUI 的新手。学习@State、@Binding、@EnvironmentObject 等新属性。

我目前正在开发登录模板,在可观察对象中定义一个绑定@Published 变量,它允许在登录页面和主页之间切换。但是,当我更新可观察对象内的变量时,主页不会显示。它仍然在登录页面中。我的代码中缺少什么?


struct ContentView: View 

    @EnvironmentObject var loginViewModel: LoginViewModel



    var body: some View 

        return Group 
            if loginViewModel.signInSuccess 
                MainPageView()
            
            else 
                LoginView(signInSuccess: $loginViewModel.signInSuccess).environmentObject(LoginViewModel())
            
        


    






final class LoginViewModel: ObservableObject  
      @Published var signInSuccess:Bool = false;

      func performLogin() 
           signInSuccess = true;
      




struct LoginView: View 

    @EnvironmentObject var loginViewModel: LoginViewModel

    @Binding var signInSuccess: Bool;

    var body: some View 

        Button(action: submit) 
                Text("Login")
                    .foregroundColor(Color.white)

        

    

    func submit() 
        loginViewModel.performLogin()
        // signInSuccess = true;
    




如果我尝试在 loginView 中更新绑定“signInSuccess”,它可以成功地将视图更新到 mainView。但是,有没有一种方法可以更新 Observable 对象中的 signInSuccess,同时将 ContentView 更新为 MainView?

【问题讨论】:

【参考方案1】:

是的,您只需将视图代码更改如下。 绑定实际上是没有必要的。

struct ContentView: View 

@EnvironmentObject var loginViewModel: LoginViewModel 

var body: some View 

    return Group 
        if loginViewModel.signInSuccess 
           MainPageView()()
        
        else 
            LoginView(signInSuccess: $loginViewModel.signInSuccess).environmentObject(self.loginViewModel)
        
    








   window.rootViewController = UIHostingController(rootView:    ContentView().environmentObject(LoginViewModel())

【讨论】:

感谢您的正确建议。我对 environmentObject 有了更好的了解。【参考方案2】:

如果你想使用环境变量,你必须在你的 SceneDelegate 中声明它并在 ContontentView 上设置它:

class SceneDelegate: UIResponder, UIWindowSceneDelegate 

    var window: UIWindow?

    var loginViewModel = LoginViewModel()

    func scene(_ scene: UIScene,
               willConnectTo session: UISceneSession,
               options connectionOptions: UIScene.ConnectionOptions) 


        let contentView = ContentView()
        if let windowScene = scene as? UIWindowScene 
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = HostingViewController(rootView: contentView.environmentObject(loginViewModel))
            self.window = window
            window.makeKeyAndVisible()
        
    
    // etc.

然后在您的 ContenView 中,您无需在 LoginView 上以任何方式设置它,因为它由环境持有:

struct ContentView: View 

    @EnvironmentObject var loginViewModel: LoginViewModel

    var body: some View 

        return Group 
            if loginViewModel.signInSuccess 
                MainPageView()
             else 
                LoginView()
            
        
    

在您的模型中,确保将您的 signInSuccess 声明为 private(set),以便它只能从类中设置并且只能从其他地方读取:

final class LoginViewModel: ObservableObject  
      @Published private(set) var signInSuccess:Bool = false;

      func performLogin() 
           signInSuccess = true;
      


最后,在 LoginView 中,您只需要包含 @EnvironmentObject,其他一切都可以工作。

struct LoginView: View 

    @EnvironmentObject var loginViewModel: LoginViewModel

    var body: some View 
        Button(action:  self.loginViewModel.performLogin() ) 
                Text("Login")
                    .foregroundColor(Color.white)
        

    

【讨论】:

以上是关于为啥可观察对象中的更新变量不会更新视图?的主要内容,如果未能解决你的问题,请参考以下文章

具有可观察集合类型的 Viewmodel 的 MVVM ListView 不更新视图

为啥 SwiftUI 模态视图在此处更新父变量(附代码)

基于设备旋转更新变量 - Swift UI

为啥我的视图没有更新我发布的变量?

可观察的集合未更新

SwiftUI,为啥部分视图没有刷新? @State 变量未更新