【问题标题】:SwiftUI - ObservableObject created multiple timesSwiftUI - 多次创建 ObservableObject
【发布时间】:2020-04-19 08:26:24
【问题描述】:

我在视图中创建了一个 ObservableObject。

@ObservedObject var selectionModel = FilterSelectionModel()

我在FilterSelectionModelinit 函数中放置了一个断点,它被多次调用。因为这个视图是NavigationLink 的一部分,所以我知道它是在随后创建的,并且与它一起创建了 selectionModel。当我导航到视图时,将再次创建 selectionModel。

在同一个视图中,我有一个“子视图”,我将 selectionModel 作为 EnvironmentObject 传递,因此子视图可以更改它。

AddFilterScreen().environmentObject(self.selectionModel)

当子视图被关闭时,selectionModel 再次被创建,对它所做的更改也消失了。

有趣的说明:最顶层是NavigationView。如果我添加

.navigationViewStyle(StackNavigationViewStyle())

对于这个NavigationView,我的 selectionModel 的更改消失了。但是如果我添加navigationStyle,则在子视图中对 selectionModel 所做的更改仍然存在!! (但我不想要拆分导航视图,我想要堆叠导航视图)

在这两种情况下——不管有没有navigationStyle,selectionModel 都会被创建多次。我无法理解这应该如何可靠地工作。

【问题讨论】:

  • 更新:我通过打破封装并将FilterSelectionModel移动到顶部ContentView“解决”了这个问题。但我不喜欢这个解决方案,因为只有我的应用程序搜索功能中涉及的一组视图才需要这个模型。整个应用不需要知道这个模型。
  • 我一直在努力解决同样的问题。不知道如何在不破坏封装的情况下优雅地解决这个问题。
  • 有什么新的更新吗?我也有同样的问题。
  • 目前没有更新。
  • 同样的问题。我最终为我的视图模型使用了单例作为解决方法...... ????

标签: navigationview swiftui observableobject


【解决方案1】:

最新的 SwiftUI 更新已经解决了这个问题。 (iOS 14 以上)

@StateObject 是我们应该使用的,而不是 @ObservedObject,但仅限于创建该对象的位置,而不是在我们传递同一对象的子视图中的所有位置。

例如-

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)")
  }
}

在上面的示例中,只有负责创建该对象的视图 (ContentView) 使用 @StateObject 和所有其他视图 (NameCount ) 共享对象正在使用@ObservedObject

通过这种方法,每当您的父视图(ContentView) 被重新创建时,User 对象将不会被重新创建,它将保持其 @State ,而您的孩子只查看 observing 到同一个 User 对象不必关心它的重新创建。

【讨论】:

    【解决方案2】:

    你可以在 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 方法,但我没有成功解决此问题。

    为了在导航视图中声明视图并且 SwiftUI 使用值类型的 struct 时停止 ViewModel 的多次初始化,因此最终这些在视图呈现之前被初始化,因此您可以将该视图转换为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() }) 
    

    【讨论】:

    • 这应该是公认的答案。 LazyView 有效。
    • 对象应该只在属性包装定义中初始化,否则在每次更新时创建视图结构时,您将初始化和销毁​​对象。
    • @malhal 你能提供一个例子,如何使用属性包装器定义来做到这一点。
    • @AnshumanSingh Apple 使用的模式(请参阅 SwiftUI WWDC 2020 中的 Data Essentials 在 20:20)是在不带参数的情况下初始化 @StateObject,然后在对象上调用一个方法,为其提供参数视图的 onAppear。
    猜你喜欢
    • 2021-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-01
    • 2021-02-27
    • 1970-01-01
    • 2021-12-11
    相关资源
    最近更新 更多