SwiftUI - 多次创建 ObservableObject
SwiftUI - ObservableObject created multiple times
我在视图中创建了一个 ObservableObject。
@ObservedObject var selectionModel = FilterSelectionModel()
我在FilterSelectionModel
的init
函数里面打了一个断点,它被调用了多次。因为这个视图是 NavigationLink
的一部分,我知道它是在随后创建的,并且与它一起创建了 selectionModel。当我导航到视图时,将再次创建 selectionModel。
在同一个视图中,我有一个 "sub View",我将 selectionModel 作为 EnvironmentObject
传递,因此子视图可以更改它。
AddFilterScreen().environmentObject(self.selectionModel)
关闭子视图后,将再次创建 selectionModel,对其所做的更改也将消失。
有趣的注释:最顶层是一个NavigationView
。如果我添加
.navigationViewStyle(StackNavigationViewStyle())
至此 NavigationView
,我的 selectionModel 的更改消失了。但是,如果我 不 添加 navigationStyle
,则 selectionModel 在子视图中所做的更改将保留!! (但我不想要拆分导航视图,我想要堆叠导航视图)
在这两种情况下 - 使用或不使用 navigationStyle
,都会多次创建 selectionModel。我无法理解这些应该如何可靠地工作。
您可以在init方法中实例化observable对象,这样您就可以保持它的值,否则该值不会消失。
在视图文件中以这种方式实例化。
@ObservedObject var selectionModel : FilterSelectionModel
init() {
selectionModel = FilterSelectionModel(value : "value to be saved from disappearing")
}
在viewModel文件中以这种方式实例化。
class FilterSelectionModel : ObservableObject {
@Published var value : String
init(value : String) {
self.value = value
}
}
这是我找到的解决方法,但 init 方法仍然被多次调用,我没有成功解决这个问题。
为了停止 ViewModels 的多次初始化,因为视图是在导航视图中声明的,而 SwiftUI 使用值类型的结构,所以最终这些在视图呈现之前被初始化,因此您可以转换该视图放入 LazyView,以便仅在即将呈现或显示视图时才对其进行初始化。
// Use this to delay instantiation when using `NavigationLink`, etc...
struct LazyView<Content: View>: View {
var content: () -> Content
var body: some View {
self.content()
}
}
你可以这样称呼它...
NavigationLink(destination: LazyView { ViewTobePresented() })
最新的 SwiftUI 更新已经解决了这个问题。 (iOS 14 岁以上)
@StateObject
是我们应该使用的,而不是 @ObservedObject
,但仅在创建 object 的地方使用,而不是在 sub-views 中我们传递相同内容的所有地方object.
例如-
class User: ObservableObject {
var name = "mohit"
}
struct ContentView: View {
@StateObject var user = User()
var body: some View {
VStack {
Text("name: \(user.name)")
NameCount(user: self.user)
}
}
}
struct NameCount: View {
@ObservedObject var user
var body: some View {
Text("count: \(user.name.count)")
}
}
在上面的示例中,只有负责创建 object 的视图 (ContentView) 将 User
object 注释为 @StateObject
和共享 object 的所有其他视图 (NameCount) 正在使用 @ObservedObject
.
通过这种方法,每当您的 parent 视图(ContentView)为 re-created 时,User
object 将不会re-created 并且它将保持其 @State,而您的 child 视图只是 observing
到相同的 User
object不必关心它的 re-creation.
我在视图中创建了一个 ObservableObject。
@ObservedObject var selectionModel = FilterSelectionModel()
我在FilterSelectionModel
的init
函数里面打了一个断点,它被调用了多次。因为这个视图是 NavigationLink
的一部分,我知道它是在随后创建的,并且与它一起创建了 selectionModel。当我导航到视图时,将再次创建 selectionModel。
在同一个视图中,我有一个 "sub View",我将 selectionModel 作为 EnvironmentObject
传递,因此子视图可以更改它。
AddFilterScreen().environmentObject(self.selectionModel)
关闭子视图后,将再次创建 selectionModel,对其所做的更改也将消失。
有趣的注释:最顶层是一个NavigationView
。如果我添加
.navigationViewStyle(StackNavigationViewStyle())
至此 NavigationView
,我的 selectionModel 的更改消失了。但是,如果我 不 添加 navigationStyle
,则 selectionModel 在子视图中所做的更改将保留!! (但我不想要拆分导航视图,我想要堆叠导航视图)
在这两种情况下 - 使用或不使用 navigationStyle
,都会多次创建 selectionModel。我无法理解这些应该如何可靠地工作。
您可以在init方法中实例化observable对象,这样您就可以保持它的值,否则该值不会消失。
在视图文件中以这种方式实例化。
@ObservedObject var selectionModel : FilterSelectionModel
init() {
selectionModel = FilterSelectionModel(value : "value to be saved from disappearing")
}
在viewModel文件中以这种方式实例化。
class FilterSelectionModel : ObservableObject {
@Published var value : String
init(value : String) {
self.value = value
}
}
这是我找到的解决方法,但 init 方法仍然被多次调用,我没有成功解决这个问题。
为了停止 ViewModels 的多次初始化,因为视图是在导航视图中声明的,而 SwiftUI 使用值类型的结构,所以最终这些在视图呈现之前被初始化,因此您可以转换该视图放入 LazyView,以便仅在即将呈现或显示视图时才对其进行初始化。
// Use this to delay instantiation when using `NavigationLink`, etc...
struct LazyView<Content: View>: View {
var content: () -> Content
var body: some View {
self.content()
}
}
你可以这样称呼它...
NavigationLink(destination: LazyView { ViewTobePresented() })
最新的 SwiftUI 更新已经解决了这个问题。 (iOS 14 岁以上)
@StateObject
是我们应该使用的,而不是 @ObservedObject
,但仅在创建 object 的地方使用,而不是在 sub-views 中我们传递相同内容的所有地方object.
例如-
class User: ObservableObject {
var name = "mohit"
}
struct ContentView: View {
@StateObject var user = User()
var body: some View {
VStack {
Text("name: \(user.name)")
NameCount(user: self.user)
}
}
}
struct NameCount: View {
@ObservedObject var user
var body: some View {
Text("count: \(user.name.count)")
}
}
在上面的示例中,只有负责创建 object 的视图 (ContentView) 将 User
object 注释为 @StateObject
和共享 object 的所有其他视图 (NameCount) 正在使用 @ObservedObject
.
通过这种方法,每当您的 parent 视图(ContentView)为 re-created 时,User
object 将不会re-created 并且它将保持其 @State,而您的 child 视图只是 observing
到相同的 User
object不必关心它的 re-creation.