【问题标题】:Updating @Published variable of an ObservableObject inside child view在子视图中更新 ObservableObject 的 @Published 变量
【发布时间】:2020-01-07 22:01:03
【问题描述】:

我在 ObservableObject 类中有一个已发布的变量isLoggedIn,如下所示:

import Combine

class UserAuth: ObservableObject{
    @Published var isLoggedIn: Bool = false
}

我想在特定视图 (LoginView) 中将此变量更新为 true。这个变量决定了我向用户显示的视图,具体取决于用户是否登录:

struct ContentView: View {
    @ObservedObject  var userAuth = UserAuth()
    var body: some View {
        Group{
            if(userAuth.isLoggedIn){
                MainView()
            }else{
                AccountView()
            }
        }
    }
}

因为 userAuth.isLoggedIn 为 false(我还没有登录)AccountView 显示出来了。

帐户视图:

struct AccountView: View {
    @State private var toggleSheet = false
    var body: some View {
        VStack {
            Spacer()
            Button(action: {
                self.toggleSheet.toggle()
            }){
                Text("Toggle Modal")
                    .padding()
                    .foregroundColor(Color.white)
                    .background(Color.blue)
                    .cornerRadius(10)
            }
            .sheet(isPresented: self.$toggleSheet){
                LoginView()
            }
            Spacer()
        }
    }
}

每当用户按下按钮时,都会显示 LoginView 模式:

struct LoginView: View {
    var body: some View {
        VStack{
            Button(action: {
                return self.login()
            }){
                Text("Log in")
                    .padding()
                    .foregroundColor(Color.white)
                    .background(Color.green)
                    .cornerRadius(10)
            }
        }
    }

    func login(){
        // update UserAuth().isLoggedIn to TRUE
    }
}

在 LoginView 中有一个按钮,我想要的逻辑是让用户按下按钮,login() 被调用,并且在该函数内部 userAuth.isLoggedIn 设置为 true。实现这一点的最佳方法是什么?

我尝试直接更改该值,但出现以下错误:

Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive

【问题讨论】:

    标签: swift swiftui swift5


    【解决方案1】:

    尝试像这样将您的代码嵌入 DispatchQueue.main.async:

    func login(){
        DispatchQueue.main.async {
            //update UserAuth().isLoggedIn to TRUE
        }
    }
    

    【讨论】:

    • 我通过将@ObservedObject var userAuth = UserAuth() 添加到LoginView 并将DispatchQueue.main.async { self.userAuth.isLoggedIn = true; } 添加到登录函数来做到这一点,但是ContentView 的预期行为不会因为@而呈现MainView() 987654327@ 设置为true ?
    【解决方案2】:

    一种可能性是将UserAuth 对象从ContentView 插入到子视图中作为EnvironmentObject

    struct ContentView: View {
        @ObservedObject var userAuth = UserAuth()
        var body: some View {
            Group {
                if userAuth.isLoggedIn {
                    MainView()
                } else {
                    AccountView()
                    .environmentObject(userAuth)
                }
            }
        }
    }
    

    现在userAuth 可以在AccountView 及其所有子视图中访问(例如LoginView):

    struct LoginView: View {
        // Access to the environment object
        @EnvironmentObject private var userAuth: UserAuth
    
        var body: some View {
            VStack {
                Button(action: {
                    return self.login()
                }) {
                    Text("Log in")
                        .padding()
                        .foregroundColor(Color.white)
                        .background(Color.green)
                        .cornerRadius(10)
                }
            }
        }
    
        func login() {
            // Update the value on the main thread
            DispatchQueue.main.async {
                self.userAuth.isLoggedIn = true
            }
        }
    }
    

    可能需要手动将 EnvironmentObject 插入LoginView。您可以通过在 AccountView 中访问它并将其插入到 LoginView 中来做到这一点:

    struct AccountView: View {
        @State private var toggleSheet = false
        // Access the value, that was inserted in `ContentView`
        @EnvironmentObject private var userAuth: UserAuth
    
        var body: some View {
            VStack {
                Spacer()
                Button(action: {
                    self.toggleSheet.toggle()
                }){
                    Text("Toggle Modal")
                        .padding()
                        .foregroundColor(Color.white)
                        .background(Color.blue)
                        .cornerRadius(10)
                }
                .sheet(isPresented: self.$toggleSheet){
                    LoginView()
                    // Insert UserAuth object again
                    .environmentObject(self.userAuth)
                }
                Spacer()
            }
        }
    }
    

    进一步阅读
    WWDC 2019 Video | Hacking with Swift Example

    【讨论】:

      猜你喜欢
      • 2021-10-14
      • 2020-07-31
      • 1970-01-01
      • 1970-01-01
      • 2021-04-21
      • 1970-01-01
      • 2021-09-07
      • 1970-01-01
      相关资源
      最近更新 更多