読者です 読者をやめる 読者になる 読者になる

Pragmatic ball boy

iOSを中心にやってる万年球拾いの老害エンジニアメモ

RxSwiftでTableView その2

iOS MVVM RxSwift Swift

rx_itemsWithCellIdentifierではSection1つしかダメでした。

複数Sectionを使う方法としていくつかあるっぽいのですが、 まずRXTableViewSectionedReloadDataSourceを使ってみます。

RXTableViewSectionedReloadDataSource

これはRxSwiftには含まれておらず別途RxDataSourcesを導入する必要があります。

ViewContorller

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 = RxTableViewSectionedReloadDataSource<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

RXTableViewSectionedReloadDataSourceをbindせる配列はSectionModelType protocolに対応している必要があります。

import RxSwift
import RxCocoa
import RxDataSources

struct MySectionModel {
  var rows: [String]

  init(rows: [String]) {
    self.rows = rows
  }
}

extension MySectionModel: SectionModelType {
  var items: [String] {
    return rows
  }

  init(original: MySectionModel, items: [String]) {
    self = original
    self.rows = items
  }
}

class MultiListViewModel {

  let items = Variable<[MySectionModel]>([])
  private var section1 = MySectionModel(rows: ["a", "b", "c"])
  private var section2 = MySectionModel(rows: ["A", "B", "C"])

  init () {
    items.value.append(section1)
    items.value.append(section2)
  }

  func change() {
    items.value[0] = MySectionModel(rows: ["x", "y", "z"])
  }
}

RXTableViewSectionedReloadDataSourceはその名の通り、Section単位でreloadData()をして表示内容を更新します。 Sectionの中身を変えたいときは上のfunc change()のようにSection毎データを更新してやらないとreloadData()が発火しないようです。

RXTableViewSectionedReloadDataSourceの使い所

  • 複数Sectionが扱いたい場合
  • Sectionを更新するときはそのSectionまるごとデータを差し替えて支障がないとき

補足

ここではSectionModelTypeプロトコルに対応させたMySectionModelを自分で定義しましたが、 RxSwiftでSectionModelというジェネリクスのstructが用意されているのでそれを使っても実装できます

public struct SectionModel<Section, ItemType>