在子视图中更新 ObservableObject 的 @Published 变量
Posted
技术标签:
【中文标题】在子视图中更新 ObservableObject 的 @Published 变量【英文标题】:Updating @Published variable of an ObservableObject inside child view 【发布时间】: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
【问题讨论】:
【参考方案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
【讨论】:
以上是关于在子视图中更新 ObservableObject 的 @Published 变量的主要内容,如果未能解决你的问题,请参考以下文章
Swiftui:子视图没有收到对 observableobject 的更新
UITextField 未更新以更改 ObservableObject (SwiftUI)
如何告诉 SwiftUI 视图绑定到多个嵌套的 ObservableObject