如何用Swift和KVO观察单个数组元素的更改(更新)?

我是否需要订阅/取消订阅数组中的单个元素?

我想单独更新每个表视图单元格以反映后备数组中的更改。 通过更改我的意思是不追加/删除操作,但更新数组的对象的属性。 我希望我能够解释我想达到的目的。 谢谢

要使用KVO,请使用dynamic属性声明模型对象:

 class Foo: NSObject { @objc dynamic var bar: String // in Swift 3, `@objc` is not necessary; in Swift 4 we must make this explicit init(bar: String) { self.bar = bar super.init() } } 

然后,让细胞处理KVO。 首先,我有一个协议,通过它可以通知表格视图,它需要重新加载:

 protocol CustomCellDelegate: class { func didUpdateObject(for cell: UITableViewCell) } 

而表视图控制器可以符合这个CustomCellDelegate协议,并在通知需要时重新加载单元格:

 func didUpdateObject(for cell: UITableViewCell) { if let indexPath = tableView.indexPath(for: cell) { tableView.reloadRows(at: [indexPath], with: .fade) } } 

所以,然后定义单元来设置和处理KVO。 在Swift 4和iOS 11中,可以使用基于闭包的observe方法和新的强types键:

 class CustomCell: UITableViewCell { weak var delegate: CustomCellDelegate? private var token: NSKeyValueObservation? var object: Foo? { willSet { token?.invalidate() } didSet { textLabel?.text = object?.bar token = object?.observe(\.bar) { [weak self] object, change in if let cell = self { cell.delegate?.didUpdateObject(for: cell) } } } } } 

在Swift 3:

 class CustomCell: UITableViewCell { private var observerContext = 0 weak var delegate: CustomCellDelegate? var object: Foo? { willSet { object?.removeObserver(self, forKeyPath: #keyPath(Foo.bar), context: &observerContext) } didSet { textLabel?.text = object?.bar object?.addObserver(self, forKeyPath: #keyPath(Foo.bar), context: &observerContext) } } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { guard context == &observerContext else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) return } delegate?.didUpdateObject(for: self) } deinit { object?.removeObserver(self, forKeyPath: #keyPath(Foo.bar), context: &observerContext) } } 

现在cellForRowAtIndexPath可以设置delegateobject属性:

 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomCell cell.delegate = self cell.object = objects![indexPath.row] return cell } 

在我看来,这个KVO机制比Swift观察者机制更好,因为模型对象不需要知道(也不应该)观察者的任何事情。 它只需要指出它支持dynamic分派,就是这样。

对于上面的Swift 2再现,请参阅此答案的以前的修订 。

您可以使用委托来处理单个单元格的数据(例如数组元素)和这些单元格的所有者(即数组的所有者)之间的通信。 “backing数组”中的更改可以传播到使用具有适当信息的委托callback所拥有的数组。

例如:

 /* delegate protocol blueprinting delegate callback methods */ protocol MyDelegate: class { func arrayEntryUpdated(element: Foo) } /* lets assume your table view cells data source are Foo objects */ class Foo { var id: Int = -1 private var bar : String = "" { didSet { /* notify the delegate each time the bar property of a Foo object is set */ delegate?.arrayEntryUpdated(self) } } weak var delegate: MyDelegate? } /* you can then hande individual updates of the Foo objects via delegate callbacks to the owner of the Foo array */ class MyArrayOwningClass: MyDelegate { var fooArr : [Foo] init(fooArr: [Foo]) { self.fooArr = fooArr self.fooArr.enumerate().forEach { $1.id = $0; $1.delegate = self } } // MyDelegate func arrayEntryUpdated(element: Foo) { print("Foo element of id #\(element.id) updated.") // ... in your case, handle individual cell updating } } 

用法示例:

 let fooArr = [Foo(), Foo(), Foo()] // "data source" let owner = MyArrayOwningClass(fooArr: fooArr) // update an element of the data source fooArr[1].bar = "bar" // via delegate: "Foo element of id #1 updated." 

您可以使用setter观察器对备份数组中的存储variables。

 var s = "myString" { willSet { // do the update on the cell here with newValue } didSet { // do something with oldValue } } var array: [String] = [] array.append(s) 

当你改变array的值时,willSet和didSet被执行,你可以调用一个函数来执行你想要的更新。