RxSwiftでTableView その3
RxTableViewSectionedReloadDataSourceでは複数Sectionに対応できましたが、 Section単位でreloadData()がされてしまっていました。
今回はRxTableViewSectionedAnimatedDataSourceを使ってみます
RxTableViewSectionedAnimatedDataSource
ViewControllerはRxTableViewSectionedReloadDataSourceとほぼ一緒で、dataSourceの型がRxTableViewSectionedAnimatedDataSourceになっただけです。
ViewController
import RxSwift import RxCocoa import RxDataSources class MultiListViewController: UIViewController { @IBOutlet weak var tableView: UITableView! @IBOutlet weak var button1: UIButton! private let viewModel = MultiListViewModel() private let dataSource = RxTableViewSectionedAnimatedDataSource<MySectionModel>() private let disposeBag = DisposeBag() override func viewDidLoad() { dataSource.configureCell = { (_, tableView, indexPath, element) in let cell = tableView.dequeueReusableCellWithIdentifier("Cell1")! cell.textLabel?.text = "\(element) @ row \(indexPath.row)" return cell } viewModel.items.asObservable() .bindTo(tableView.rx_itemsWithDataSource(dataSource)) .addDisposableTo(disposeBag) button1.rx_tap .bindNext { [weak self] in self?.viewModel.change() } .addDisposableTo(disposeBag) } }
ViewModel
dataSourceとbindingさせる配列はAnimatableSectionModelType protocolに対応している必要があります。
更にAnimatableSectionModelTypeのitemはIdentificableType protocolに対応している必要があります。これはitemが変化したかどうか判定するために使われます。
struct MySectionModel { var header: String var items: [Item] } extension MySectionModel : AnimatableSectionModelType { typealias Item = String var identity: String { return header } init(original: MySectionModel, items: [Item]) { self = original self.items = items } } extension String: IdentifiableType { public typealias Identity = String public var identity : Identity { return self } } class MultiListViewModel { let items = Variable<[MySectionModel]>([]) var section1 = MySectionModel(header: "section1", items: ["a", "b", "c"]) var section2 = MySectionModel(header: "section2", items: ["d", "e", "f"]) init () { items.value.append(section1) items.value.append(section2) } func change() { items.value[0] = MySectionModel(header: "section1", items: ["a", "b", "c", "z"]) } }
変更があった場合、内部ではtableViewのperformBatchUpdatesが実行されるので変化のあったcellだけ変更がかかります。
アニメーションを変更したい場合はAnimationContigurationを変更してやればよいです。
dataSource.animationConfiguration = AnimationConfiguration(insertAnimation: .Right, reloadAnimation: .Right, deleteAnimation: .Right)
RxTableViewSectionedAnimatedDataSourceの使い所
- cellの挿入や削除などでアニメーションを入れたい場合
- 変化があった場合にSectionやTableView全体をreloadDataしたくない場合
ただ気になるところが、内部的な変化の判定するために、変更前後の配列の比較を行っているので効率的とはいえません。 利用する側にとっては何も考えずに配列ごと上書きしてしまえばよいのですが、すごい大きい配列になった場合にパフォーマンスとか大丈夫なのかなと懸念がありそうな気がします。