はじめに
以前はできなかった、UITableViewのデータが変わったときのアニメーションの変更が、v4.0.2でできるようになりました。
この記事ではそのやり方を簡単に解説しています。
ちなみにUICollectionViewはまだ未対応で、PRもでていません。
この記事は2015-10-24時点の情報です。
やり方
BNDTableViewProxyDataSource
を継承したクラスを作成し、アニメーションの設定を行うメソッドを実装して、このクラスのインスタンスをtableViewのdataSourceのbind時に渡すだけです。
1. BNDTableViewProxyDataSource
を継承したクラスの作成
class ABCTableViewProxyDataSource : BNDTableViewProxyDataSource {
@objc func tableView(tableView: UITableView, animationForRowAtIndexPaths indexPaths: [NSIndexPath]) -> UITableViewRowAnimation {
return .None
}
@objc func tableView(tableView: UITableView, animationForRowInSections sections: Set<Int>) -> UITableViewRowAnimation {
return .None
}
}
2. bindToする際にproxyDataSource
に先ほどのクラスのインスタンスを渡す
class ABCTableViewController {
...
// アニメーションが必要な際に都度参照されるので、こんな感じで保持しておく
let proxy = ABCTableViewProxyDataSource()
...
override func viewDidLoad() {
dataSource.bindTo(tableView, proxyDataSource:self.proxy) { (indexPath, dataSource, tableView) -> UITableViewCell in
...
return cell
}
...
}
}
that's all☆
簡単な解説
dataSource.bindTo(tableView) { indexPath, arrays, tableView -> UITableViewCell in
...
return cell
}
このようにbindしたときに、デフォルトではBNDTableViewDataSource
というクラスが呼ばれてあれこれとTableViewのDataSource系のメソッドをよしなにやってくれています。
その中で以下の部分がアニメーションの設定がなされている部分です。
...
private class func applySectionUnitChangeSet(changeSet: ObservableArrayEventChangeSet, tableView: UITableView, dataSource: BNDTableViewProxyDataSource?) {
switch changeSet {
case .Inserts(let indices):
tableView.insertSections(NSIndexSet(set: indices), withRowAnimation: dataSource?.tableView?(tableView, animationForRowInSections: indices) ?? .Automatic)
case .Updates(let indices):
tableView.reloadSections(NSIndexSet(set: indices), withRowAnimation: dataSource?.tableView?(tableView, animationForRowInSections: indices) ?? .Automatic)
case .Deletes(let indices):
tableView.deleteSections(NSIndexSet(set: indices), withRowAnimation: dataSource?.tableView?(tableView, animationForRowInSections: indices) ?? .Automatic)
}
}
private class func applyRowUnitChangeSet(changeSet: ObservableArrayEventChangeSet, tableView: UITableView, sectionIndex: Int, dataSource: BNDTableViewProxyDataSource?) {
switch changeSet {
case .Inserts(let indices):
let indexPaths = indices.map { NSIndexPath(forItem: $0, inSection: sectionIndex) }
tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: dataSource?.tableView?(tableView, animationForRowAtIndexPaths: indexPaths) ?? .Automatic)
case .Updates(let indices):
let indexPaths = indices.map { NSIndexPath(forItem: $0, inSection: sectionIndex) }
tableView.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: dataSource?.tableView?(tableView, animationForRowAtIndexPaths: indexPaths) ?? .Automatic)
case .Deletes(let indices):
let indexPaths = indices.map { NSIndexPath(forItem: $0, inSection: sectionIndex) }
tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: dataSource?.tableView?(tableView, animationForRowAtIndexPaths: indexPaths) ?? .Automatic)
}
}
...
ようはdataSourceがnilでなく、指定されているメソッドが実装されていたら、それを実行、でなければデフォルトの値を返す、ということを行っています。
ここで使われているdataSource
という変数はbindTo時の、proxyDataSource
という引数で渡されてくるものが入ってきています。
さらにproxyDataSource
は、BNDTableViewProxyDataSource
に準じていて、BNDTableViewProxyDataSource
に以下のようにアニメーションの設定を返すメソッドが定められています。
@objc public protocol BNDTableViewProxyDataSource {
/// Override to specify custom row animation when row is being inserted, deleted or updated
optional func tableView(tableView: UITableView, animationForRowAtIndexPaths indexPaths: [NSIndexPath]) -> UITableViewRowAnimation
/// Override to specify custom row animation when section is being inserted, deleted or updated
optional func tableView(tableView: UITableView, animationForRowInSections sections: Set<Int>) -> UITableViewRowAnimation
}
そんなわけで、proxyDataSourceで渡す変数のクラスに、上記の2つのメソッドを実装しておけば、アニメーションが変更できるようになるわけです。
メソッドを実行する際に、option引数としてhashを渡して、値があれば内部の処理を変える、といったことをするように、上記のようなクラスを渡して、そのクラスインスタンスにメソッドがあれば実行する、というシンプルな発想です。名前どおり、proxyしてくれています。