記事の目的
RxDataSourcesを用いたUITableViewで、特定の条件に該当するセルを編集不可にするのに時間がかかったため、本記事でまとめておこうと思います。
実装
セルの編集可否は canEditRowAtIndexPath のクロージャー内で実装します。コードは以下の通りです。
self.dataSource = RxTableViewSectionedReloadDataSource<SectionOfCustomData>(
configureCell: { (ds: TableViewSectionedDataSource<SectionOfCustomData>, tableView: UITableView, indexPath: IndexPath, model: CustomData) -> UITableViewCell in
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = model.str
return cell
}, canEditRowAtIndexPath: { datasource, indexPath in
let data = datasource.sectionModels[indexPath.section].items
if data[indexPath.row].str == "zero" {
return false
}
return true
}, canMoveRowAtIndexPath: { _, _ in
return false
}
)
こちらのコードでは、data[indexPath.row].str == "zero"
であるセルが編集不可となります。
コード全体は以下の通りです。
なお、RxDataSourcesを用いてサンプルとなるTableViewを作成する部分の実装は、こちらのサイトのコードを参考にさせていただきました。
ViewController.swift
import UIKit
import RxSwift
import RxCocoa
import RxDataSources
class ViewController: UIViewController, UITableViewDelegate {
@IBOutlet weak var tableView: UITableView!
@IBAction func editButton(_ sender: UIButton) {
self.tableView.setEditing(true, animated: true)
}
@IBAction func cancelButton(_ sender: UIButton) {
self.tableView.setEditing(false, animated: true)
}
var dataSource: RxTableViewSectionedReloadDataSource<SectionOfCustomData>!
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView()
tableView.rx.setDelegate(self).disposed(by: self.disposeBag)
setupDataSource()
bindModels()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(false, animated: true)
navigationController?.setToolbarHidden(false, animated: true)
}
func setupDataSource() {
self.dataSource = RxTableViewSectionedReloadDataSource<SectionOfCustomData>(
configureCell: { (ds: TableViewSectionedDataSource<SectionOfCustomData>, tableView: UITableView, indexPath: IndexPath, model: CustomData) -> UITableViewCell in
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = model.str
return cell
}, canEditRowAtIndexPath: { datasource, indexPath in
let data = datasource.sectionModels[indexPath.section].items
if data[indexPath.row].str == "zero" {
return false
}
return true
}, canMoveRowAtIndexPath: { _, _ in
return false
}
)
self.dataSource.titleForHeaderInSection = { ds, index in
return ds.sectionModels[index].header
}
}
func bindModels() {
let sections = [
SectionOfCustomData(header: "First section",
items: [CustomData(str: "zero"),
CustomData(str: "one")]
),
SectionOfCustomData(header: "Second section",
items: [CustomData(str: "two"),
CustomData(str: "zero"),
CustomData(str: "three")]
)
]
Observable.just(sections)
.bind(to: tableView.rx.items(dataSource: self.dataSource))
.disposed(by: self.disposeBag)
}
}
SectionOfCustomData.swift
import Foundation
import RxDataSources
struct CustomData {
var str: String
}
struct SectionOfCustomData {
var header: String
var items: [Item]
}
extension SectionOfCustomData: SectionModelType {
typealias Item = CustomData
init(original: SectionOfCustomData, items: [SectionOfCustomData.Item]) {
self = original
self.items = items
}
}
注意点
上記のコードで、"zero" のセルを編集不可にできましたが、編集モード中に "zero" のセルをタップすることは可能なため、セルタップ時には選択済のセルとしてカウントされます。
そのため、上記コードを tableView.allowsMultipleSelectionDuringEditing = true
で使用する場合、セル選択時の処理で tableView.indexPathsForSelectedRows
から該当のセルのindexPath を取り除く処理が必要です。