カテゴリー
iOS RxSwift Swift

RxSwiftとUITableViewを連携し、複数のSectionのTableViewを作成

RxSwiftとUITableViewの設定は少し分かりづらかったので、ここにまとめてみます。

Sample Code on github

まず、”SectionModel”というものを作成します。これは、UITableViewをいくつかのセクションに分ける場合の、いくつかのセクションと、そのセクション内のTableViewCellの内容に対応しています。

// ConfigViewSectionModelはSectionModel (RxDatasourcesで定義されているstruct)のtypealiasとして作成します。

typealias ConfigViewSectionModel = SectionModel<ConfigViewSection, ConfigItem>

// ConfigViewSectionはenumとして作成し、それぞれのセクションに応じて作成します。この例では"parameter", "playType", "interval"という3つのセクションに対応しています。
enum ConfigViewSection {
	
	// number of sections as needed ...
	case parameter
	case playType
	case interval
	
}

// ConfigItemもenumとして作成
enum ConfigItem {
	
	// parameter section
	case gameMode
	case tempo
	case timeLimit
	case tries
	case counts
	case lowest
	case highest
	case pianoVolume
	case metronomevolume
	
	// type section
	case ascend
	case descend
	case quarterNote
	case eighthNote
	case dyad
	
	// interval section
	case minSecond
	case second
	case minThird
	case third
	case fourth
	case dimFifth
	case fifth
	case minSixth
	case sixth
	case minSeventh
	case seventh
	case octave
}

ViewControllerでは以下のように設定

class ConfigViewController: UIViewController {

  lazy var tableView: UITableView = {
    let tableView = UITableView()
    // カスタムTableViewCellはいくつでもRegister可能 (Identifierで管理)
    tableView.register(ConfigParameterTVCell.self, forCellReuseIdentifier: ConfigParameterTVCell.identifier)
    tableView.register(ConfigSwitchTVCell.self, forCellReuseIdentifier: ConfigSwitchTVCell.identifier)
    // セパレーターを消したい場合は.clearを指定
    tableView.separatorColor = UIColor.clear
    // AutoLayoutのためこのパラメータをfalseにする
    tableView.translatesAutoresizingMaskIntoConstraints = false
    return tableView
  }()

  lazy var datasource = RxTableViewSectionedReloadDataSource<ConfigViewSectionModel>(configureCell: configureCell)
	
  lazy var configureCell: RxTableViewSectionedReloadDataSource<ConfigViewSectionModel>.ConfigureCell = { [weak self] (dataSource, tableView, indexPath, _) in
    // indexPathによりdataSourceからitem (enum型のConfigItem)をゲットする
    let item = dataSource[indexPath]
    // itemをswitchする
    switch item {
      case .tempo:
        // まず、事前にregisterしてあるCustomTableViewCell (この場合はConfigParameterTVCell) をdequeueする
        let cell = tableView.dequeueReusableCell(withIdentifier: ConfigParameterTVCell.identifier, for: indexPath) as! ConfigParameterTVCell
        // その後、cellの各パラメータに対して処理を記載
        cell.label.text = "something something"
        return cell
      case .timeLimit:
        // 同様にcellをdequeueし処理する

        // 以下省略
  }
}

RxSwiftのSubject (この場合はBehaviorRelay)とのDataBindingは以下のようにします。

private func setupViewModel() {
  configItems.asObservable()
    .bind(to: tableView.rx.items(dataSource: datasource))
    .disposed(by: bag)
}

上記のconfigItemsは以下のように定義し、設定します。

// configItemsは[ConfigViewSectionModel]のBehaviorRelayで、初期値は空の配列
let configItems = BehaviorRelay<[ConfigViewSectionModel]>(value: [])

// 3つのセクションに応じてそれぞれConfigViewSectionModelを作成します。
func updateConfigItems() {
  let sections: [ConfigViewSectionModel] = [
    parameterSection(),
    playTypeSection(),
    intervalSection(),
  ]
  // configItemsに[ConfigViewSectionModel]を渡します
  configItems.accept(sections)
}

// parameterSectionの実装
func parameterSection() -> ConfigViewSectionModel {
  let items: [ConfigItem] = [
    .gameMode,
    .tempo,
    .timeLimit,
    .tries,
    .counts,
    .lowest,
    .highest,
    .pianoVolume,
    .metronomevolume,
  ]
  // このSectionModelのConstructorはRxDatasoucesで定義されている
  return ConfigViewSectionModel(model: .parameter, items: items)
}

// playTypeSectionの実装
func playTypeSection() -> ConfigViewSectionModel {
  let items: [ConfigItem] = [
    .ascend,
    .descend,
    .quarterNote,
    .eighthNote,
    .dyad,
  ]
  // このSectionModelのConstructorはRxDatasoucesで定義されている
  return ConfigViewSectionModel(model: .playType, items: items)
}

// intervalSectionの実装
func intervalSection() -> ConfigViewSectionModel {
  let items: [ConfigItem] = [
    .minSecond,
    .second,
    .minThird,
    .third,
    .fourth,
    .dimFifth,
    .fifth,
    .minSixth,
    .sixth,
    .minSeventh,
    .seventh,
    .octave,
  ]
  // このSectionModelのConstructorはRxDatasoucesで定義されている
  return ConfigViewSectionModel(model: .interval, items: items)
}