RxSwift與UITableView的使用,之前文章簡單的實現了一下。今天主要是實現如何處理UITableViewCell上的按鈕事件。以前我們通常會選擇使用代理、閉包、通知這些方法中的一個方法,我個人喜歡閉包。但是現在已經開始使用RxSwift,所以一起來看看RxSwift中我們如何處理按鈕事件。
UITableViewCell的內部事件
一般對於cell的點擊,會觸發其代理方法:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// 處理cell的點擊事件
}
在Rx中,我們處理如下: tableView.rx.modelSelected(ItemModel.self)
.subscribe(onNext: { value in
print(value)
}).disposed(by: disposeBag)
或者先獲取點擊的位置,然後在根據點擊的位置獲取數據 tableView.rx.itemSelected.asObservable()
.subscribe(onNext: { index in
print(index)
}).disposed(by: disposeBag)
由上可知對於cell的處理還是很簡單的。
UITableViewCell上的按鈕事件
比較好的方案就是閉包,前面也談到了。那麼我們如何使用Rx來處理呢?先看一個demo
自定義cell部分
該部分內容很簡單,cell上添加了一個label和button,並進行佈局
import UIKit
import SnapKit
import RxSwift
class CustomTableViewCell: UITableViewCell {
private(set) var disposeBag = DisposeBag()
lazy var info: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.font = UIFont.systemFont(ofSize: 20)
return label
}()
lazy var button: UIButton = {
let btn = UIButton(type: .custom)
btn.setTitle("button", for: .normal)
btn.setTitleColor(UIColor.black, for: .normal)
btn.backgroundColor = UIColor.cyan
return btn
}()
override func prepareForReuse() {
super.prepareForReuse()
disposeBag = DisposeBag()
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupUI()
setupConstraints()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI() {
addSubview(info)
addSubview(button)
}
private func setupConstraints() {
info.snp.makeConstraints { (make) in
make.left.top.right.equalToSuperview().inset(15)
}
button.snp.makeConstraints { (make) in
make.centerX.equalToSuperview()
make.top.equalTo(info.snp.bottom).offset(15)
make.size.equalTo(CGSize(width: 100, height: 50))
make.bottom.equalToSuperview().offset(-15)
}
}
}
這裏有一個關鍵的地方:prepareForReuse()方法,在方法裏面,每次重用cell的時候,我們會釋放之前的disposeBag, 然後會爲cell創建一個新的disposeBag對象,這樣可以保證cell被重用的時候不會被多次訂閱,造成錯誤。
視圖控制器部分
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
let titles = ["自己選擇的路,跪着也要把它走完。", "平凡的腳步也可以走完偉大的行程,世上沒有絕望的處境,只有對處境絕望的人", "世上沒有絕望的處境,只有對處境絕望的人", "第一個青春是上帝給的;第二個青春是靠自己努力的。", "每個人都會累,沒人能爲你承擔所有悲傷,人總有一段時間要學會自己長大。", "每天醒來,敲醒自己的不是鐘聲,而是夢想。", "這個世界到處充滿着不公平,我們能做的不僅僅是接受,還要試着做一些反抗。"]
lazy var tableView: UITableView = {
let tv = UITableView(frame: CGRect.zero, style: .plain)
tv.dataSource = self
tv.register(CustomTableViewCell.self, forCellReuseIdentifier: "cell")
tv.estimatedRowHeight = 100
tv.rowHeight = UITableViewAutomaticDimension
return tv
}()
lazy var headView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.red
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
}
func setupTableView() {
view.addSubview(tableView)
tableView.snp.makeConstraints { (make) in
make.left.right.top.bottom.equalToSuperview()
}
headView.frame = CGRect(x: 0, y: 0, width: view.bounds.size.width, height: 300)
tableView.tableHeaderView = headView
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return titles.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell
cell.info.text = "\(titles[indexPath.row]): \(indexPath.row)"
cell.button.rx.tap.asObservable().subscribe(onNext: {
print("\(indexPath.row)")
}).disposed(by: cell.disposeBag)
return cell
}
}
很簡單就是添加一個tableView設置相應的屬性,並訂閱點擊事件。這樣就可以正確處理按鈕事件了。然後我們可以簡化一下代碼:
class ViewController: UIViewController {
let disposeBag = DisposeBag()
let titles = ["自己選擇的路,跪着也要把它走完。", "平凡的腳步也可以走完偉大的行程,世上沒有絕望的處境,只有對處境絕望的人", "世上沒有絕望的處境,只有對處境絕望的人", "第一個青春是上帝給的;第二個青春是靠自己努力的。", "每個人都會累,沒人能爲你承擔所有悲傷,人總有一段時間要學會自己長大。", "每天醒來,敲醒自己的不是鐘聲,而是夢想。", "這個世界到處充滿着不公平,我們能做的不僅僅是接受,還要試着做一些反抗。"]
lazy var tableView: UITableView = {
let tv = UITableView(frame: CGRect.zero, style: .plain)
tv.estimatedRowHeight = 100
tv.register(CustomTableViewCell.self, forCellReuseIdentifier: "cell")
tv.rowHeight = UITableViewAutomaticDimension
return tv
}()
lazy var headView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.red
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
bindTableView()
}
func setupTableView() {
view.addSubview(tableView)
tableView.snp.makeConstraints { (make) in
make.left.right.top.bottom.equalToSuperview()
}
headView.frame = CGRect(x: 0, y: 0, width: view.bounds.size.width, height: 300)
tableView.tableHeaderView = headView
}
func bindTableView() {
Observable.of(titles)
.bind(to: tableView.rx.items(cellIdentifier: "cell", cellType: CustomTableViewCell.self)) { (indexPath, element, cell) in
cell.info.text = element
cell.button.rx.tap.asObservable().subscribe(onNext: {
print(element)
}).disposed(by: cell.disposeBag)
}.disposed(by: disposeBag)
}
}
參考
Invoked multiple times in UITableView