RxSwift - UITableViewCell上按鈕事件處理

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)
    }
}

參考

在實踐中應用RxSwift3處理cell

Invoked multiple times in UITableView

Avoid duplicate Subscription


發佈了229 篇原創文章 · 獲贊 42 · 訪問量 47萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章