概要
前回はRxDataSourceを使って基本的なUITableViewのテンプレートを作ってみました。
今回はこのRxDataSourceについて弄ってみてどんな事ができるのかについてより詳しくみていこうと思います。
基本的なstoryboardの配置やソースコードの配置は前回のままですので念の為コピーしたものを載せておきます。
Main.storyboard
rxdatasource
ViewController.swift
import UIKit
import RxSwift
import RxCocoa
import RxDataSources
/// TableViewCellに紐付けるDataModel
struct CustomCellModel {
var name: String
var email: String
}
/// セクションヘッダーの名前とセクション内のitem
struct SectionOfCustomData {
/// セクションヘッダーの名前
var header: String
/// indexPath.row のcellのデータ
var items: [Item]
}
/// RxDataSourceを使ってDataModelとdataSourceを紐づけるため
extension SectionOfCustomData: SectionModelType {
// Item に CellのDataModelを紐づける
typealias Item = CustomCellModel
// ほぼテンプレで可能
init(original: SectionOfCustomData, items: [Item]) {
self = original
self.items = items
}
}
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
let disposeBag = DisposeBag()
let dataSource = RxTableViewSectionedReloadDataSource<SectionOfCustomData>(
configureCell: { dataSource, tableView, indexPath, item in
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = item.name
return cell
})
// 複数のsectionを表示させたい場合はSectionOfCustomDataを生成していきます。
// SectionOfCustomData の
let sections = [
SectionOfCustomData(header: "1st section",
items: [CustomCellModel(name: "山田花子", email: "hanako@gmail.com"),
CustomCellModel(name: "田中太郎", email: "taro@gmail.com"),
CustomCellModel(name: "石田真一", email: "shinichi@gmail.com")]),
]
override func viewDidLoad() {
super.viewDidLoad()
dataSource.titleForHeaderInSection = { dataSource, index in
return dataSource.sectionModels[index].header
}
Observable.just(sections) // sectionsを生成して
.bind(to: tableView.rx.items(dataSource: dataSource)) // itemsのdataSourceに紐づける
.disposed(by: disposeBag)
}
}
これが前回までのソースコードとなります。
今回はこれの変更を加えます。
HeaderViewの追加
試しにHeaderViewを追加してみます。RxDataSourceのライブラリのReadmeを確認すると
RxDataSource.swift
/// HeaderViewの作成
dataSource.titleForHeaderInSection = { dataSource, index in
return dataSource.sectionModels[index].header
}
/// FooterViewの作成
dataSource.titleForFooterInSection = { dataSource, indexPath in
return dataSource.sectionModels[index].footer
}
このようになっています。
そのため、viewDidLoad()
のコードを次のように修正してみます。
ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
// HeaderViewを追加する
dataSource.titleForHeaderInSection = { dataSource, index in
return dataSource.sectionModels[index].header
}
Observable.just(sections) // sectionsを生成して
.bind(to: tableView.rx.items(dataSource: dataSource)) // itemsのdataSourceに紐づける
.disposed(by: disposeBag)
}
このように修正してアプリをビルドしてみます。
tableView_headerview
このように無事にヘッダーが表示されたら成功です。
FooterViewの追加
では、次にCustomなFooterViewの追加を実装してみます。
まずはSectionOfCustomData
を次のように修正してみます。
ViewController.swift
/// セクションヘッダーの名前とセクション内のitem
struct SectionOfCustomData {
/// セクションヘッダーの名前
var header: String
/// indexPath.row のcellのデータ
var items: [Item]
/// セクションフッターの名前
var footer: String
}
セクションフッターの名前を追加しました。
これによって変数sectionsの修正が必要になります。
ViewController.swift
// 複数のsectionを表示させたい場合はSectionOfCustomDataを生成していきます。
let sections = [
SectionOfCustomData(header: "1st section",
items: [CustomCellModel(name: "山田花子", email: "hanako@gmail.com"),
CustomCellModel(name: "田中太郎", email: "taro@gmail.com"),
CustomCellModel(name: "石田真一", email: "shinichi@gmail.com")],
footer: "1st section footer"), // footer: を追加しました
]
これによってsectionsのデータ構造が変わってfooterも追加されます。
ですが、これだけではUITableViewには反映されません。
最後にviewDidLoad()
をこのように修正します。
ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
// HeaderViewを追加する
dataSource.titleForHeaderInSection = { dataSource, index in
return dataSource.sectionModels[index].header
}
// FooterViewを追加する
dataSource.titleForFooterInSection = { dataSource, index in
return dataSource.sectionModels[index].footer
}
Observable.just(sections) // sectionsを生成して
.bind(to: tableView.rx.items(dataSource: dataSource)) // itemsのdataSourceに紐づける
.disposed(by: disposeBag)
}
これでアプリをビルドしてみましょう。
次のようにfooterが追加表示されていたら成功です。
tableView_footer
Footerが表示されるようになりました。
このようにRxDataSourceを上手く使えば普通のUITableViewのようにデータを扱えるようになります。
Qiitaの記事ではRxDataSourceの使い方よりもそれを意識した設計方針の方が難しく感じますね。
RxDataSourceに苦手意識を持っている僕のような方であれば今回の記事を参考にして最低限の実装方法を理解すれば自ずと
設計方針が見えてくると思います。
とりあえず、これでUITableViewの実装は一通り経験できたので頑張れそうだと思います。
全体のソースコード
一応、念の為、全体のソースコードを乗せておきます。
ViewController.swift
import UIKit
import RxSwift
import RxCocoa
import RxDataSources
/// TableViewCellに紐付けるDataModel
struct CustomCellModel {
var name: String
var email: String
}
/// セクションヘッダーの名前とセクション内のitem
struct SectionOfCustomData {
/// セクションヘッダーの名前
var header: String
/// indexPath.row のcellのデータ
var items: [Item]
/// セクションフッターの名前
var footer: String
}
/// RxDataSourceを使ってDataModelとdataSourceを紐づけるため
extension SectionOfCustomData: SectionModelType {
// Item に CellのDataModelを紐づける
typealias Item = CustomCellModel
// ほぼテンプレで可能
init(original: SectionOfCustomData, items: [Item]) {
self = original
self.items = items
}
}
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
let disposeBag = DisposeBag()
let dataSource = RxTableViewSectionedReloadDataSource<SectionOfCustomData>(
configureCell: { dataSource, tableView, indexPath, item in
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = item.name
return cell
})
// 複数のsectionを表示させたい場合はSectionOfCustomDataを生成していきます。
let sections = [
SectionOfCustomData(header: "1st section",
items: [CustomCellModel(name: "山田花子", email: "hanako@gmail.com"),
CustomCellModel(name: "田中太郎", email: "taro@gmail.com"),
CustomCellModel(name: "石田真一", email: "shinichi@gmail.com")],
footer: "1st section footer"),
]
override func viewDidLoad() {
super.viewDidLoad()
// HeaderViewを追加する
dataSource.titleForHeaderInSection = { dataSource, index in
return dataSource.sectionModels[index].header
}
// FooterViewを追加する
dataSource.titleForFooterInSection = { dataSource, index in
return dataSource.sectionModels[index].footer
}
Observable.just(sections) // sectionsを生成して
.bind(to: tableView.rx.items(dataSource: dataSource)) // itemsのdataSourceに紐づける
.disposed(by: disposeBag)
}
}
なんやかんやでちょっとずつ複雑になってきましたね。