我创建了在更复杂的应用程序中遇到的问题的简化版本。因此,您可以帮助我理解为什么事情没有按我的预期工作,而不是建议我重写代码吗?代码以这种奇怪的方式构建是有原因的,以类似于更复杂的情况。这是简化的测试代码 - 顶层视图是 TestEnvUpdate:
class GlobData: ObservableObject {
var buttonText: String? = "hello"
static let shared = GlobData()
func setText(str: String?) {
buttonText = str
objectWillChange.send()
}
}
struct TestEnvUpdate: View {
var body: some View {
ParentView().environmentObject(GlobData.shared)
}
}
struct ParentView: View {
@EnvironmentObject var globData: GlobData
var body: some View {
if globData.buttonText != nil {
ChildView()
} else {
Text("buttonText is nil")
}
}
}
struct ChildView: View {
@EnvironmentObject var globData: GlobData
var body: some View {
VStack {
Text(globData.buttonText!) // error occurs here
Button ("set to nil") { globData.setText(str: nil) }
}
}
}
当我按下“设置为 nil”按钮时,GlobData setText 函数将运行,并且 objectWillChange.send() 函数将运行。然后,我期望 ParentView 主体函数运行,请注意,buttonText 设置为零,并且不尝试创建 ChildView,因为 ParentView 依赖于环境对象。但相反,ChildView 运行并崩溃,因为 ButtonText 为零。
任何人都可以解释为什么会发生这种情况,以及 objectWillChange.send() 的排序如何工作吗?使用 objectWillChange.send() 时是否存在任何线程问题或任何延迟操作?
我有一种感觉,这个问题的答案将加深我对 SwiftUI 在视图层次结构中排序视图创建方式的理解。
globData
是 ChildView
的依赖项。当 SwiftUI 观察到 globData
发生变化时(因为您调用了 objectWillChange.send()
),它需要调用 ChildView.body
来查看发生了什么变化。
是的,无论如何,
ChildView
都会消失,因为if
中的ParentView
,但这并不重要。 ChildView
中可能存在需要动画化的更改,或者需要运行的 onDisappear
等。
至于
body
的调用顺序,是从父级到子级。您可以通过在 let _ = print("...")
中放置一些 body
语句来看到这一点。
旁注:您可以使用
objectWillChange
属性包装器来包装 buttonText
,而不是发送 @Published
。