RxSwift + MJRefresh 打造自動處理重新整理控制元件狀態

LinXunFeng發表於2017-10-23

本文是基於 iOS - RxSwift 專案實戰記錄 所述,如果你還未閱讀過,建議你最好還先閱讀一遍,並下載Demo熟悉一下 : )

LXFBiliBili

前言

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 的重新整理狀態

image.png

開源庫

LXFProtocolTool

推薦使用 LXFProtocolTool/Refreshable ,會不斷更新,功能更加強大

pod 'LXFProtocolTool/Refreshable'
複製程式碼

案例

協議:Refreshable.swift

ViewModel:LXFLiveViewModel

Controller:LXFLiveViewController

LXFBiliBili

微信公眾號

相關文章