如何用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
可以设置delegate
和object
属性:
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被执行,你可以调用一个函数来执行你想要的更新。