RxSwiftを導入したあるアプリの制作中、既存のUITableViewDelegateのeditActionsForRowAtを利用したカスタムアクションボタンような機能を実現しようすることをメモしておきます。
既存のUITableViewDelegateでの実装は大抵以下の様子:
EventListViewController.swift
class EventListViewController: UITableViewController {
// MARK: - View LifeCircle
override func viewDidLoad() {
super.viewDidLoad()
// table cell を初期化
self.tableView.register(UINib(resource: R.nib.eventCell), forCellReuseIdentifier: R.reuseIdentifier.eventCell.identifier)
self.tableView.delegate = self
self.tableView.dataSource = self
}
// ... ...
// MARK: - UITableViewDataSource
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return mockEventList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: R.reuseIdentifier.eventCell, for: indexPath) else {
return UITableViewCell()
}
let item = self.mockEventList[indexPath.row]
cell.configure(viewModel: item)
return cell
}
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// TODO: check edit conditions
if checkEdit {
return false
}
return true
}
// MARK: - UITableViewDelegate
override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
// ここのカスタマイズしたいアクションをを登録する
let action1 = UITableViewRowAction(style: .normal, title: "edit", handler: { action, _ in
print("edit action")
})
let action2 = UITableViewRowAction(style: .normal, title: "delete", handler: { action, _ in
print(" delete action")
})
action1.backgroundColor = UIColor.red
action2.backgroundColor = UIColor.clear
return [action1, action2]
}
}
通常なら、上記のデリゲートを利用することで実現できるが、
RxSwiftを利用したリアクティブ開発では、RxCocoaから拡張されたライブラリRxDataSourcesを利用して上記機能を実現させます。
1) まずは RxDataSourcesのSectionModelTypeプロトーコルに準拠する構造体を定義する
EventSectionItem.swift
import Foundation
import RxDataSources
enum EventSection {
case events(title: String, items: [EventSectionItem])
}
enum EventSectionItem {
case eventItem(cellViewModel: EventCellViewModel)
}
extension EventSection: SectionModelType {
var title: String {
switch self {
case .events(let title, _): return title
}
}
var items: [EventSectionItem] {
switch self {
case .events(_, let items): return items.map {$0}
}
}
init(original: EventSection, items: [EventSectionItem]) {
switch original {
case .events(let title, let items): self = .events(title: title, items: items)
}
}
}
2) Rx関連初期登録を行う
EventListViewController.swift
import RxSwift
import RxCocoa
import RxDataSources // <- 今度利用するライブラリ
class EventListViewController: UITableViewController {
// DisposeBagの登録
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
//... ...
// Cell 初期登録
let reuseIdentifier = R.reuseIdentifier.eventCell.identifier
self.tableView.register(UINib(resource: R.nib.eventCell), forCellReuseIdentifier: reuseIdentifier)
// デリケードを登録 (dataSourceは以下RxDataSourcesを利用して登録するためnilで初期化)
self.tableView.dataSource = nil
self.tableView.rx.setDelegate(self).disposed(by: disposeBag)
}
}
3) 引き続きRxのdataSourceの初期処理と登録
EventListViewController.swift
class EventListViewController: UITableViewController {
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
// dataSourceを定義
let dataSource = RxTableViewSectionedReloadDataSource<EventSection>(configureCell: { dataSource, tableView, indexPath, item in
switch item {
case .eventItem(let cellViewModel):
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as? EventCell
// cell.bind(to: cellViewModel)
cell?.configure(viewModel: cellViewModel)
return cell ?? UITableViewCell()
}
}, titleForHeaderInSection: { dataSource, index in
let section = dataSource[index]
return section.title
}, canEditRowAtIndexPath: {_, _ in
return true // 一旦can Editできるよう trueを返す
})
}
}
4) 最後は, 通常と同じくUITableViewDelegateのeditActionsForRowAtメソットをoverrideする
EventListViewController.swift
class EventListViewController: UITableViewController {
// ... ...
// MARK: - UITableViewDelegate
override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
guard let curUser = UserService.default.getUser() else {
return [] // not logged
}
// canEditableの条件判断をここで実現する
// 実現方法は以下URLの記事を参照ください
// https://qiita.com/ENIX/items/b0871a5961b3a27c32fa
if checkEdit {
return[]
}
// カスタマイズしたいアクションをを同じくここで登録する
let action1 = UITableViewRowAction(style: .normal, title: "edit", handler: { action, _ in
print("edit action")
})
let action2 = UITableViewRowAction(style: .normal, title: "delete", handler: { action, _ in
print(" delete action")
})
action1.backgroundColor = UIColor.red
action2.backgroundColor = UIColor.clear
return [action1, action2]
}
}
以上、大変お疲れ様でした。
もしもっと良い実現案がございましたら、共有いただけると大変嬉しいですね。
iOS、Androidアプリの制作なら、hq7781@gmail.comまで、
法人並みに、信頼且つ満足できる製品を納品いたします