本文是基於 iOS - RxSwift 專案實戰記錄 所述,如果你還未閱讀過,建議你最好還先閱讀一遍,並下載Demo熟悉一下 : )
前言
MVVM的模式中,多出了ViewModel這個角色,將邏輯處理、網路請求等繁雜操作中ViewController中抽離出來,ViewController得以瘦身。 結合RxSwift架構,我們一般就會在ViewModel中定義一個input收集繁雜操作所需的資訊,通過一個transform方法將input作為引數傳入,進而得到一個output供controller使用。
在使用RxSwift開發時會大量的使用到這種形式,其中就包括我們的網路請求。 結合 iOS - RxSwift 專案實戰記錄 中所述的“MJRefresh在RxSwift中的使用”,在output中定義了一個變數
let refreshStatus = Variable<LXFRefreshStatus>(.none)
複製程式碼
controller通過output將其進行監聽,從而當值發生變化時,controller就能實時獲取當前應所處的重新整理狀態
vmOutput.refreshStatus.asObservable().subscribe(onNext: {[weak self] status in
switch status {
case .beingHeaderRefresh:
self?.tableView.mj_header.beginRefreshing()
case .endHeaderRefresh:
self?.tableView.mj_header.endRefreshing()
case .beingFooterRefresh:
self?.tableView.mj_footer.beginRefreshing()
case .endFooterRefresh:
self?.tableView.mj_footer.endRefreshing()
case .noMoreData:
self?.tableView.mj_footer.endRefreshingWithNoMoreData()
default:
break
}
}).addDisposableTo(rx_disposeBag)
複製程式碼
如果在一個專案多處使用到了這種方式,我們就可以看到弊端——重複程式碼,過於冗餘。
難道我們每次都要在controller中進行如此操作嗎?
面向協議
關於協議的內容可以看下我之前的這兩篇文章 iOS - Swift 面向協議程式設計(一) iOS - Swift 面向協議程式設計(二)
總結協議的兩大作用:1、規範 2、定製能力
定義協議 Refreshable
/* ============================ Refreshable ================================ */
// 需要使用 MJExtension 的控制器使用
protocol Refreshable {
}
extension Refreshable where Self : UIViewController {
func initRefreshHeader(_ scrollView: UIScrollView, _ action: @escaping () -> Void) -> MJRefreshHeader {
scrollView.mj_header = MJRefreshNormalHeader(refreshingBlock: { action() })
return scrollView.mj_header
}
func initRefreshFooter(_ scrollView: UIScrollView, _ action: @escaping () -> Void) -> MJRefreshFooter {
scrollView.mj_footer = MJRefreshAutoNormalFooter(refreshingBlock: { action() })
return scrollView.mj_footer
}
}
複製程式碼
在controller中遵循 Refreshable 協議,通過initRefreshHeader方法或者initRefreshFooter方法給tableView或者collectionView賦予頭部或尾部重新整理的能力,並且書寫下拉重新整理時需要執行的程式碼
// 以下拉重新整理為例
let refreshHeader = initRefreshHeader(liveCollectionView) { [weak self] in
// 下拉後需要執行的操作
self?.vmOutput?.requestCommand.onNext(())
}
複製程式碼
接下來再講講output,只要有網路請求的地方,就會需要需要監聽請求狀態,既然這樣,那麼可以為output定義一個協議OutputRefreshProtocol,專門用來規範必需宣告的屬性
/* ============================ OutputRefreshProtocol ================================ */
// viewModel 中 output使用
protocol OutputRefreshProtocol {
// 告訴外界的tableView當前的重新整理狀態
var refreshStatus : Variable<LXFRefreshStatus> {get}
}
複製程式碼
接著讓output去遵循該協議,並進行初始化重新整理狀態的值為.none
struct LXFLiveOutput: OutputRefreshProtocol {
var refreshStatus: Variable<LXFRefreshStatus>
let sections: Driver<[LXFLiveSection]>
init(sections: Driver<[LXFLiveSection]>) {
self.sections = sections
refreshStatus = Variable<LXFRefreshStatus>(.none)
}
}
複製程式碼
到此為止,其實跟之前沒啥兩樣,只是使controller更方便初始化重新整理控制元件而已。接下來才是本文的重點。
重點
重新整理的狀態無非也就那麼幾種,下拉過載資料,上拉載入更多,請求完成時結束下拉或上拉等等。。。那我們何必要在每個controller中再去管理這等瑣事?? 而至此,重新整理控制元件的狀態是由變數 refreshStatus 來決定,此時 refreshStatus 又宣告在 OutputRefreshProtocol 協議中,我們何不再定義一個方法,將重新整理控制元件的狀態交給refreshStatus自己來幫我們處理呢~
extension OutputRefreshProtocol {
func autoSetRefreshHeaderStatus(header: MJRefreshHeader?, footer: MJRefreshFooter?) -> Disposable {
return refreshStatus.asObservable().subscribe(onNext: { (status) in
switch status {
case .beingHeaderRefresh:
header?.beginRefreshing()
case .endHeaderRefresh:
header?.endRefreshing()
case .beingFooterRefresh:
footer?.beginRefreshing()
case .endFooterRefresh:
footer?.endRefreshing()
case .noMoreData:
footer?.endRefreshingWithNoMoreData()
default:
break
}
})
}
}
複製程式碼
這時需要我們將重新整理控制元件的物件 header / footer 傳入到方法中,實現自動控制重新整理控制元件狀態。
總結使用
一、output中遵守協議 OutputRefreshProtocol, 並初始化 refreshStatus 的值為 none
struct LXFLiveOutput: OutputRefreshProtocol {
var refreshStatus: Variable<LXFRefreshStatus>
let sections: Driver<[LXFLiveSection]>
init(sections: Driver<[LXFLiveSection]>) {
self.sections = sections
refreshStatus = Variable<LXFRefreshStatus>(.none)
}
}
複製程式碼
二、controller 遵守協議 Refreshable,通過協議中的方法初始化重新整理控制元件及對應的操作,並將重新整理控制元件物件作為引數傳入到自動處理狀態方法中
extension LXFLiveViewController: Refreshable
複製程式碼
let refreshHeader = initRefreshHeader(liveCollectionView) { [weak self] in
self?.vmOutput?.requestCommand.onNext(())
}
vmOutput?.autoSetRefreshHeaderStatus(header: refreshHeader, footer: nil).disposed(by: rx.disposeBag)
複製程式碼
三、viewModel中根據實際情況實時更新 refreshStatus 的重新整理狀態
開源庫
推薦使用 LXFProtocolTool/Refreshable
,會不斷更新,功能更加強大
pod 'LXFProtocolTool/Refreshable'
複製程式碼
案例
ViewModel:LXFLiveViewModel
Controller:LXFLiveViewController