很多情況下,表格里的數(shù)據(jù)不是一開始就準備好的、或者固定不變的??赡芪覀冃枰认蚍?wù)器請求數(shù)據(jù),再將獲取到的內(nèi)容顯示在表格中。
要重新加載表格數(shù)據(jù),過去的做法就是調(diào)用tableView的reloadData()方法。本文介紹在使用RxSwift的情況下,應(yīng)該如何刷新數(shù)據(jù)。
三、數(shù)據(jù)刷新
1,效果圖
(1)界面初始化完畢后,tableView 默認會加載一些隨機數(shù)據(jù)。
(2)點擊右上角的刷新按鈕,tableView 會重新加載并顯示一批新數(shù)據(jù)。
(3)為方便演示,每次獲取數(shù)據(jù)不是真的去發(fā)起網(wǎng)絡(luò)請求。而是在本地生成后延遲 2 秒返回,模擬這種異步請求的情況。


2,樣例代碼
import UIKit
import RxSwift
import RxCocoa
import RxDataSources
class ViewController: UIViewController {
//刷新按鈕
@IBOutlet weak var refreshButton: UIBarButtonItem!
//表格
var tableView:UITableView!
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
//創(chuàng)建表格視圖
self.tableView = UITableView(frame: self.view.frame, style:.plain)
//創(chuàng)建一個重用的單元格
self.tableView!.register(UITableViewCell.self,
forCellReuseIdentifier: "Cell")
self.view.addSubview(self.tableView!)
//隨機的表格數(shù)據(jù)
let randomResult = refreshButton.rx.tap.asObservable()
.startWith(()) //加這個為了讓一開始就能自動請求一次數(shù)據(jù)
.flatMapLatest(getRandomResult)
.share(replay: 1)
//創(chuàng)建數(shù)據(jù)源
let dataSource = RxTableViewSectionedReloadDataSource
<SectionModel<String, Int>>(configureCell: {
(dataSource, tv, indexPath, element) in
let cell = tv.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = "條目\(indexPath.row):\(element)"
return cell
})
//綁定單元格數(shù)據(jù)
randomResult
.bind(to: tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
}
//獲取隨機數(shù)據(jù)
func getRandomResult() -> Observable<[SectionModel<String, Int>]> {
print("正在請求數(shù)據(jù)......")
let items = (0 ..< 5).map {_ in
Int(arc4random())
}
let observable = Observable.just([SectionModel(model: "S", items: items)])
return observable.delay(2, scheduler: MainScheduler.instance)
}
}
3,防止表格多次刷新的說明
(1)flatMapLatest 的作用是當在短時間內(nèi)(上一個請求還沒回來)連續(xù)點擊多次“刷新”按鈕,雖然仍會發(fā)起多次請求,但表格只會接收并顯示最后一次請求。避免表格出現(xiàn)連續(xù)刷新的現(xiàn)象。
//隨機的表格數(shù)據(jù)
let randomResult = refreshButton.rx.tap.asObservable()
.startWith(()) //加這個為了讓一開始就能自動請求一次數(shù)據(jù)
.flatMapLatest(getRandomResult)
.share(replay: 1)
(2)我們也對源頭進行限制下。即通過 throttle 設(shè)置個閥值(比如 1 秒),如果在1秒內(nèi)有多次點擊則只取最后一次,那么自然也就只發(fā)送一次數(shù)據(jù)請求。
//隨機的表格數(shù)據(jù)
let randomResult = refreshButton.rx.tap.asObservable()
.throttle(1, scheduler: MainScheduler.instance) //在主線程中操作,1秒內(nèi)值若多次改變,取最后一次
.startWith(()) //加這個為了讓一開始就能自動請求一次數(shù)據(jù)
.flatMapLatest(getRandomResult)
.share(replay: 1)
附:停止數(shù)據(jù)請求
? ? ? ? 在實際項目中我們可能會需要對一個未完成的網(wǎng)絡(luò)請求進行中斷操作。比如切換頁面或者分類時,如果上一次的請求還未完成就要將其取消掉。下面通過樣例演示如何實現(xiàn)該功能。
1,效果圖
? ? ? ?這里我們在前面樣例的基礎(chǔ)上增加了個“停止”按鈕。當發(fā)起請求且數(shù)據(jù)還未返回時(2 秒內(nèi)),按下該按鈕后便會停止對結(jié)果的接收處理,即表格不加載顯示這次的請求數(shù)據(jù)。

2,樣例代碼
? ? ? ?該功能簡單說就是通過 takeUntil 操作符實現(xiàn)。當 takeUntil 中的 Observable 發(fā)送一個值時,便會結(jié)束對應(yīng)的 Observable。
import UIKit
import RxSwift
import RxCocoa
import RxDataSources
class ViewController: UIViewController {
//刷新按鈕
@IBOutlet weak var refreshButton: UIBarButtonItem!
//停止按鈕
@IBOutlet weak var cancelButton: UIBarButtonItem!
//表格
var tableView:UITableView!
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
//創(chuàng)建表格視圖
self.tableView = UITableView(frame: self.view.frame, style:.plain)
//創(chuàng)建一個重用的單元格
self.tableView!.register(UITableViewCell.self,
forCellReuseIdentifier: "Cell")
self.view.addSubview(self.tableView!)
//隨機的表格數(shù)據(jù)
let randomResult = refreshButton.rx.tap.asObservable()
.startWith(()) //加這個為了讓一開始就能自動請求一次數(shù)據(jù)
.flatMapLatest{
self.getRandomResult().takeUntil(self.cancelButton.rx.tap)
}
.share(replay: 1)
//創(chuàng)建數(shù)據(jù)源
let dataSource = RxTableViewSectionedReloadDataSource
<SectionModel<String, Int>>(configureCell: {
(dataSource, tv, indexPath, element) in
let cell = tv.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = "條目\(indexPath.row):\(element)"
return cell
})
//綁定單元格數(shù)據(jù)
randomResult
.bind(to: tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
}
//獲取隨機數(shù)據(jù)
func getRandomResult() -> Observable<[SectionModel<String, Int>]> {
print("正在請求數(shù)據(jù)......")
let items = (0 ..< 5).map {_ in
Int(arc4random())
}
let observable = Observable.just([SectionModel(model: "S", items: items)])
return observable.delay(2, scheduler: MainScheduler.instance)
}
}