【RxSwift 實踐系列 3/3】thinking in Rx- UITableView

lizyyy發表於2018-03-05

上期:【RxSwift 實踐系列 2/3】thinking in Rx- Create和Drive的功能,我們要實現在uitableview中cell上按鈕功能。同樣,我們在cell上直接訂閱這個按鈕,當接受到點選請求的時候,UserDefaults上去

如下:

searchFiled.rx.text
            .filter{
                ($0?.lengthOfBytes(using: .utf8))! > 0 //長度大於1
            }
            .throttle(0.5, scheduler: MainScheduler.instance) //延遲0.5秒再執行
            .flatMap{
                ListViewModel().searchFromApi(repositoryName:String(describing: $0!))
            }
            .asDriver(onErrorJustReturn: [])
            .drive(resultTableView.rx.items(cellIdentifier: "SearchTableViewCell", cellType: SearchTableViewCell.self)) {
                (tableView, element, cell) in
                cell.nameLabel.text = element.name
                cell.codeLabel.text = element.code
                cell.addBtn.rx.tap.subscribe(
                    onNext: {
                        var stocks = UserDefaults.standard.array(forKey: "stockSelected")  as! [String]
                        stocks.append(element.code)
                        UserDefaults.standard.set(stocks, forKey: "stockSelected")
                        cell.addBtn.setTitle("已關注", for: .normal)
                    }
                    ).disposed(by: rx.disposeBag) 
            }
            .disposed(by: rx.disposeBag)
複製程式碼

但是實際操作上發現問題了,當搜尋框輸入在不斷變化的時候,cell也是在不斷的重新整理的,cell重用的時候,按鈕就會被多次訂閱,造成嚴重的bug。這裡就要用到一個方法:prepareForReuse

    private(set) var disposeBag = DisposeBag()
    //SearchTableViewCell中實現
    override func prepareForReuse() {
        super.prepareForReuse()
        disposeBag = DisposeBag()
    }
複製程式碼

在每次重用cell的時候,我們會釋放之前的disposeBag,然後會為cell建立一個新的disposeBag物件,避免多次訂閱.

cell.addBtn.rx.tap.subscribe(
        onNext: {
            var stocks = UserDefaults.standard.array(forKey: "stockSelected")  as! [String]
            stocks.append(element.code)
            UserDefaults.standard.set(stocks, forKey: "stockSelected")
            cell.addBtn.setTitle("已關注", for: .normal)
        }
        ).disposed(by: cell.disposeBag)//注意,這裡是cell的disposeBag
複製程式碼

這樣我們就實現了cell上按鈕的實現。

image.png

最後就是首頁列表展示了,首頁的展示和搜尋列表一樣,這裡就不囉嗦了,這裡有個難點是,股票的價格在不重新整理tableview的情況下更新,在平常的實現裡我們只能使用重新整理單個cell來實現了,來看看rx的實現方式:

ListViewModel().getFromApi(repositoryName: "").subscribe(
    onNext:{ rs in Observable<[ListModel]>
        .just(rs)
        .bind(to: self.resultTableView.rx.items(cellIdentifier: "ListTableViewCell", cellType: ListTableViewCell.self)) {(_, model:ListModel, cell:ListTableViewCell) in
            
            //定時任務每兩秒執行一次
            Observable<Int>.interval(2, scheduler: MainScheduler.instance).subscribe { (event) in
                ListViewModel().getOne(repositoryName: model.symbol)
                .subscribe(
                    onNext:{ rs in Observable<ListModel>
                        .just(rs)
                        .subscribe{
                            cell.currentLabel.text = String(describing: rs.current)
                            cell.nameLabel.text = rs.name
                            cell.codeLabel.text = rs.code
                        }.disposed(by: self.rx.disposeBag)
                    }
                ).disposed(by: self.rx.disposeBag)
            }.disposed(by: self.rx.disposeBag)
        }.disposed(by: self.rx.disposeBag)
    }
).disposed(by: rx.disposeBag)

複製程式碼

在繫結的cell裡面,對整個cell做一個定時任務interval,每秒獲取一次網路資料,訂閱這個資料來更新cell的元素,從而實現了cell無重新整理的更新。可以說程式碼上只增加了3行就實現了這個功能!

image.png

這個用rxSwift實現的新增股票程式碼實時重新整理價格的應用到這裡就完成了,可以在這裡找到例項程式碼。

程式碼裡面還有很多缺陷和需要優化的地方: 比如很多的if else可以用rxSwift做的更簡潔、許多錯誤異常等並沒有處理,網路授權的提示、新增股票程式碼後首頁並沒有及時更新。大家可以嘗試著用rxSwift繼續優化程式碼。

三期連結:

【RxSwift 實踐系列 1/3】為什麼使用RxSwift

【RxSwift 實踐系列 2/3】thinking in Rx- Create和Drive

【RxSwift 實踐系列 3/3】thinking in Rx- UITableView

相關文章