三最铁、上拉加載的樣例
1讯赏,效果圖
(1)頁面打開后會自動加載 15 條數(shù)據(jù)垮兑,并顯示在表格中。
(2)而每次上拉表格又會隨機(jī)生成 15 條新的數(shù)據(jù)漱挎,并拼接到原數(shù)據(jù)下方顯示系枪。
2,樣例代碼
(1)ViewModel.swift
由于“加載更多”功能需要把新數(shù)據(jù)添加到老數(shù)據(jù)尾部磕谅,這里我使用 BehaviorRelay
作為表格數(shù)據(jù)序列私爷,因?yàn)樗梢垣@取到之前的數(shù)據(jù)。
import RxSwift
import RxCocoa
class ViewModel {
//表格數(shù)據(jù)序列
let tableData = BehaviorRelay<[String]>(value: [])
//停止上拉加載刷新狀態(tài)序列
let endFooterRefreshing: Driver<Bool>
//ViewModel初始化(根據(jù)輸入實(shí)現(xiàn)對應(yīng)的輸出)
init(footerRefresh: Driver<Void>,
dependency: (
disposeBag:DisposeBag,
networkService: NetworkService )) {
//上拉結(jié)果序列
let footerRefreshData = footerRefresh
.startWith(()) //初始化完畢時(shí)會自動加載一次數(shù)據(jù)
.flatMapLatest{ return dependency.networkService.getRandomResult() }
//生成停止上拉加載刷新狀態(tài)序列
self.endFooterRefreshing = footerRefreshData.map{ _ in true }
//上拉加載時(shí)膊夹,將查詢到的結(jié)果拼接到原數(shù)據(jù)底部
footerRefreshData.drive(onNext: { items in
self.tableData.accept(self.tableData.value + items )
}).disposed(by: dependency.disposeBag)
}
}
(2)ViewController.swift
ViewModel
初始化后衬浑,將表格數(shù)據(jù)序列綁定到 tableView
上顯示數(shù)據(jù)。同時(shí)將停止刷新序列綁定到 tableView
的 mj_footer
上讓其自動停止刷新放刨。
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
//表格
var tableView:UITableView!
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
//創(chuàng)建表格視圖
self.tableView = UITableView(frame: self.view.frame, style:.plain)
//創(chuàng)建一個(gè)重用的單元格
self.tableView!.register(UITableViewCell.self,
forCellReuseIdentifier: "Cell")
self.view.addSubview(self.tableView!)
//設(shè)置尾部刷新控件
self.tableView.mj_footer = MJRefreshBackNormalFooter()
//初始化ViewModel
let viewModel = ViewModel(
footerRefresh: self.tableView.mj_footer.rx.refreshing.asDriver(),
dependency: (
disposeBag: self.disposeBag,
networkService: NetworkService()))
//單元格數(shù)據(jù)的綁定
viewModel.tableData.asDriver()
.drive(tableView.rx.items) { (tableView, row, element) in
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = "\(row+1)工秩、\(element)"
return cell
}
.disposed(by: disposeBag)
//上拉刷新狀態(tài)結(jié)束的綁定
viewModel.endFooterRefreshing
.drive(self.tableView.mj_footer.rx.endRefreshing)
.disposed(by: disposeBag)
}
}
四、下拉刷新 + 上拉加載的樣例
1宏榕,效果圖
(1)頁面打開后會自動加載 15 條數(shù)據(jù)拓诸,并顯示在表格中。
(2)當(dāng)下拉表格時(shí)會隨機(jī)生成 15 條新的數(shù)據(jù)麻昼,并替換表格里的原數(shù)據(jù)奠支。
(3)而當(dāng)上拉表格又會隨機(jī)生成 15 條新的數(shù)據(jù),并拼接到原數(shù)據(jù)下方顯示抚芦。
2倍谜,樣例代碼
(1)ViewModel.swift
這里同樣使用 BehaviorRelay
作為表格數(shù)據(jù)序列,同時(shí)還定義了兩個(gè)停止刷新序列(分別表示停止下拉刷新叉抡、停止上拉刷新)尔崔。
import RxSwift
import RxCocoa
class ViewModel {
//表格數(shù)據(jù)序列
let tableData = BehaviorRelay<[String]>(value: [])
//停止頭部刷新狀態(tài)
let endHeaderRefreshing: Driver<Bool>
//停止尾部刷新狀態(tài)
let endFooterRefreshing: Driver<Bool>
//ViewModel初始化(根據(jù)輸入實(shí)現(xiàn)對應(yīng)的輸出)
init(input: (
headerRefresh: Driver<Void>,
footerRefresh: Driver<Void> ),
dependency: (
disposeBag:DisposeBag,
networkService: NetworkService )) {
//下拉結(jié)果序列
let headerRefreshData = input.headerRefresh
.startWith(()) //初始化時(shí)會先自動加載一次數(shù)據(jù)
.flatMapLatest{ return dependency.networkService.getRandomResult() }
//上拉結(jié)果序列
let footerRefreshData = input.footerRefresh
.flatMapLatest{ return dependency.networkService.getRandomResult() }
//生成停止頭部刷新狀態(tài)序列
self.endHeaderRefreshing = headerRefreshData.map{ _ in true }
//生成停止尾部刷新狀態(tài)序列
self.endFooterRefreshing = footerRefreshData.map{ _ in true }
//下拉刷新時(shí),直接將查詢到的結(jié)果替換原數(shù)據(jù)
headerRefreshData.drive(onNext: { items in
self.tableData.accept(items)
}).disposed(by: dependency.disposeBag)
//上拉加載時(shí)褥民,將查詢到的結(jié)果拼接到原數(shù)據(jù)底部
footerRefreshData.drive(onNext: { items in
self.tableData.accept(self.tableData.value + items )
}).disposed(by: dependency.disposeBag)
}
}
(2)ViewController.swift
ViewModel
初始化后季春,將表格數(shù)據(jù)序列綁定到 tableView
上顯示數(shù)據(jù)。同時(shí)將兩個(gè)停止刷新序列分別綁定到 tableView
的 mj_header
和 mj_footer
上讓其自動停止刷新消返。
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
//表格
var tableView:UITableView!
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
//創(chuàng)建表格視圖
self.tableView = UITableView(frame: self.view.frame, style:.plain)
//創(chuàng)建一個(gè)重用的單元格
self.tableView!.register(UITableViewCell.self,
forCellReuseIdentifier: "Cell")
self.view.addSubview(self.tableView!)
//設(shè)置頭部刷新控件
self.tableView.mj_header = MJRefreshNormalHeader()
//設(shè)置尾部刷新控件
self.tableView.mj_footer = MJRefreshBackNormalFooter()
//初始化ViewModel
let viewModel = ViewModel(
input: (
headerRefresh: self.tableView.mj_header.rx.refreshing.asDriver(),
footerRefresh: self.tableView.mj_footer.rx.refreshing.asDriver()),
dependency: (
disposeBag: self.disposeBag,
networkService: NetworkService()))
//單元格數(shù)據(jù)的綁定
viewModel.tableData.asDriver()
.drive(tableView.rx.items) { (tableView, row, element) in
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = "\(row+1)载弄、\(element)"
return cell
}
.disposed(by: disposeBag)
//下拉刷新狀態(tài)結(jié)束的綁定
viewModel.endHeaderRefreshing
.drive(self.tableView.mj_header.rx.endRefreshing)
.disposed(by: disposeBag)
//上拉刷新狀態(tài)結(jié)束的綁定
viewModel.endFooterRefreshing
.drive(self.tableView.mj_footer.rx.endRefreshing)
.disposed(by: disposeBag)
}
}
附:“下拉刷新 + 上拉加載”的功能改進(jìn)
1,功能說明
在前面的樣例中下拉刷新和上拉加載這兩個(gè)行為是獨(dú)立的撵颊,互不影響宇攻。也就是說當(dāng)我們下拉刷新后,在數(shù)據(jù)返回前又進(jìn)行了次上拉操作倡勇,那么之后表格便會連續(xù)刷新兩次逞刷,影響體驗(yàn)。這里對功能做個(gè)改進(jìn):
- 當(dāng)下拉刷新時(shí),如果數(shù)據(jù)還未返回夸浅。這時(shí)進(jìn)行上拉加載會取消前面的下拉刷新操作(包括下拉刷新的數(shù)據(jù))仑最,只進(jìn)行上拉數(shù)據(jù)的加載。
- 同樣的题篷,當(dāng)上拉加載時(shí)词身,如果數(shù)據(jù)還未放回。這時(shí)進(jìn)行下拉刷新會取消上拉加載操作(包括上拉加載的數(shù)據(jù))番枚,只進(jìn)行下拉數(shù)據(jù)的加載法严。
同時(shí)這次我們不使用 Driver
這個(gè)特征序列,而是用普通的 Observable
序列葫笼。
2深啤,樣例代碼
(1)NetworkService.swift
import RxSwift
import RxCocoa
//網(wǎng)絡(luò)請求服務(wù)
class NetworkService {
//獲取隨機(jī)數(shù)據(jù)
func getRandomResult() -> Observable<[String]> {
print("正在請求數(shù)據(jù)......")
let items = (0 ..< 15).map {_ in
"隨機(jī)數(shù)據(jù)\(Int(arc4random()))"
}
let observable = Observable.just(items)
return observable
.delay(2, scheduler: MainScheduler.instance)
}
}
(2)ViewModel.swift
import RxSwift
import RxCocoa
class ViewModel {
//表格數(shù)據(jù)序列
let tableData = BehaviorRelay<[String]>(value: [])
//停止頭部刷新狀態(tài)
let endHeaderRefreshing: Observable<Bool>
//停止尾部刷新狀態(tài)
let endFooterRefreshing: Observable<Bool>
//ViewModel初始化(根據(jù)輸入實(shí)現(xiàn)對應(yīng)的輸出)
init(input: (
headerRefresh: Observable<Void>,
footerRefresh: Observable<Void> ),
dependency: (
disposeBag:DisposeBag,
networkService: NetworkService )) {
//下拉結(jié)果序列
let headerRefreshData = input.headerRefresh
.startWith(()) //初始化時(shí)會先自動加載一次數(shù)據(jù)
.flatMapLatest{ _ in
dependency.networkService.getRandomResult()
.takeUntil(input.footerRefresh)
}.share(replay: 1) //讓HTTP請求是被共享的
//上拉結(jié)果序列
let footerRefreshData = input.footerRefresh
.flatMapLatest{ _ in
dependency.networkService.getRandomResult()
.takeUntil(input.headerRefresh)
}.share(replay: 1) //讓HTTP請求是被共享的
//生成停止頭部刷新狀態(tài)序列
self.endHeaderRefreshing = Observable.merge(
headerRefreshData.map{ _ in true },
input.footerRefresh.map{ _ in true }
)
//生成停止尾部刷新狀態(tài)序列
self.endFooterRefreshing = Observable.merge(
footerRefreshData.map{ _ in true },
input.headerRefresh.map{ _ in true }
)
//下拉刷新時(shí),直接將查詢到的結(jié)果替換原數(shù)據(jù)
headerRefreshData.subscribe(onNext: { items in
self.tableData.accept(items)
}).disposed(by: dependency.disposeBag)
//上拉加載時(shí)路星,將查詢到的結(jié)果拼接到原數(shù)據(jù)底部
footerRefreshData.subscribe(onNext: { items in
self.tableData.accept(self.tableData.value + items )
}).disposed(by: dependency.disposeBag)
}
}
(3)ViewController.swift
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
//表格
var tableView:UITableView!
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
//創(chuàng)建表格視圖
self.tableView = UITableView(frame: self.view.frame, style:.plain)
//創(chuàng)建一個(gè)重用的單元格
self.tableView!.register(UITableViewCell.self,
forCellReuseIdentifier: "Cell")
self.view.addSubview(self.tableView!)
//設(shè)置頭部刷新控件
self.tableView.mj_header = MJRefreshNormalHeader()
//設(shè)置尾部刷新控件
self.tableView.mj_footer = MJRefreshBackNormalFooter()
//初始化ViewModel
let viewModel = ViewModel(
input: (
headerRefresh: self.tableView.mj_header.rx.refreshing.asObservable() ,
footerRefresh: self.tableView.mj_footer.rx.refreshing.asObservable()),
dependency: (
disposeBag: self.disposeBag,
networkService: NetworkService()))
//單元格數(shù)據(jù)的綁定
viewModel.tableData
.bind(to: tableView.rx.items) { (tableView, row, element) in
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = "\(row+1)溯街、\(element)"
return cell
}
.disposed(by: disposeBag)
//下拉刷新狀態(tài)結(jié)束的綁定
viewModel.endHeaderRefreshing
.bind(to: self.tableView.mj_header.rx.endRefreshing)
.disposed(by: disposeBag)
//上拉刷新狀態(tài)結(jié)束的綁定
viewModel.endFooterRefreshing
.bind(to: self.tableView.mj_footer.rx.endRefreshing)
.disposed(by: disposeBag)
}
}