将更改发布到可观察对象的集合
Posted
技术标签:
【中文标题】将更改发布到可观察对象的集合【英文标题】:Publishing changes to a collection of observable objects 【发布时间】:2021-06-23 15:41:56 【问题描述】:我在传播视图模型中保存在数组中的对象上发生的更改时遇到问题。
我知道如果集合本身发生变化,集合的@Published
将起作用(例如,如果元素是struct
而不是class
)。假设我需要将类保留为类。有没有办法将事件传播到视图,以便它知道应该刷新它。
我一直在尝试所有令人讨厌的方法,例如实现 ObservableCollection
或 ObservableArray
,但似乎没有任何效果。
下面是我正在努力解决的一个例子。
Toggle 正在更改具有所有 ObservableObject
一致性和 @Published
注释但仍没有刷新 Text 的数组的内部元素。
import SwiftUI
import Combine
struct ContentView: View
@StateObject var vm = ViewModel()
var body: some View
Text(vm.texts.first!.text)
.padding()
Button("Toggle")
vm.texts.first?.toggle()
class ViewModel: ObservableObject
@Published var texts: [TextHolder] = [.init(), .init()]
class TextHolder: ObservableObject
@Published var text: String = ""
func toggle()
text = UUID().uuidString
struct ContentView_Previews: PreviewProvider
static var previews: some View
ContentView()
【问题讨论】:
三个答案中的任何一个是否回答了您的问题或提供了任何帮助?我注意到他们都没有任何赞成票或 cmets。 【参考方案1】:您的方法的问题是 TextHolder 是一个类,它是引用类型,如果您更改其中的任何值,更改将不会反映到数组,这就是 SwiftUI 视图未更新的原因。
方法一:
您可以将 TextHolder 从 class 更改为 struct,如果您更改 struct 中的任何值,则会创建一个新副本,您的数组将获得更改为您的 SwiftUI 视图。
请尝试以下代码
struct ContentView: View
@StateObject var vm = ViewModel()
var body: some View
Text(vm.texts.first!.text)
.padding()
Button("Toggle")
vm.texts[0].toggle()
class ViewModel: ObservableObject
@Published var texts: [TextHolder] = [.init(), .init()]
struct TextHolder
var text: String = ""
mutating func toggle()
text = UUID().uuidString
方法二:
更改值后,您必须手动告诉您的 viewModel 某些内容已更改,请刷新。
Button("Toggle")
vm.texts[0].toggle()
vm.objectWillChange.send()
希望能帮助你理解。
【讨论】:
【参考方案2】:注意:这是基于您列出的“假设我需要将类保留为类”的要求——否则,将您的模型设为结构会免费为您提供所有这些行为。
您可以在ObservableObject
上手动拨打objectWillChange.send()
。例如:
Button("Toggle")
vm.texts.first?.toggle()
vm.objectWillChange.send()
主要缺点包括必须在每个突变位点添加代码来调用它,并且实际上要记住这样做。您可以做一些事情来划分代码,就像将toggle
移动到父对象并将索引传递给它——然后,您可以将所有objectWillChange
调用保留在父对象中。此外,您可以尝试使用 KVO 来观察子对象的属性,并在看到其中一个对象发生变化时调用 objectWillChange
。
【讨论】:
【参考方案3】:如果您无法将类转换为结构,您可以采取的一种方法是订阅数组中项目的所有 objectWillChange
发布者,并在其中一个对象时为主模型发出一个改变:
@Published var texts: [TextHolder] = [.init(), .init()]
didSet
updateTextsSubscriptions()
private var textsSubscriptions = [AnyCancellable]()
private func updateTextsSubscriptions()
textsSubscriptions = texts.map
$0.objectWillChange.sink(receiveValue:
self.objectWillChange.send()
)
您还需要在初始化程序中调用 updateTextsSubscriptions
,以确保 texts
数组的任何初始值都受到监控:
init()
updateTextsSubscriptions()
【讨论】:
以上是关于将更改发布到可观察对象的集合的主要内容,如果未能解决你的问题,请参考以下文章