MJRefresh 是 OC 語言里面算是一款比較通用的刷新框架了,可以用于 UITableView 和 UICollectionView 的刷新,從而可以很方便的完成下拉刷新和上拉加載更多。而且可定制程度也很高。
最近需要在公司的項(xiàng)目上加入一個刷新的特效,而公司項(xiàng)目用的刷新框架是 MJefresh ,所以在完成需求的同時就順便把 MJRefresh 框架的源碼給看了一遍。然后寫下這篇文章總結(jié)一下。
MJRefresh框架的結(jié)構(gòu)是用子類繼承的方式實(shí)現(xiàn)的,創(chuàng)建一個基類MJRefreshComponent,然后通過繼承的方式,讓MJRefreshHeader和MJRefreshFooter分別具備下拉刷新和上拉加載的功能。從繼承機(jī)構(gòu)來看可以分為三層,具體的繼承關(guān)系可以從下面的圖里看:

其中五大核心類庫
MJRefreshComponent
MJRefreshHeader
MJRefreshFooter
MJRefreshBackFooter
MJRefreshAutoFooter
其它的類都是在上面的基礎(chǔ)上自定義的類。
首先來看一下MJRefresh框架的基類:MJRefreshComponent
MJRefreshComponent
這個類作為框架的基類,涵蓋了基類所具備的狀態(tài),回調(diào)block等,完成了以下幾件事情:
布局控件的位置。
完成基本的設(shè)置工作。
設(shè)置下拉刷新時的文本框。
設(shè)置控件的所有狀態(tài)。
設(shè)置回調(diào)函數(shù)。
添加監(jiān)聽。
提供刷新,停止刷新接口。
提供子類需要實(shí)現(xiàn)的方法。
具體實(shí)現(xiàn):
定義控件的全部的狀態(tài)

定義控件的回調(diào)函數(shù)

Target-Action類型的回調(diào)

監(jiān)聽函數(shù)的實(shí)現(xiàn)

開始刷新和停止刷新

需要子類實(shí)現(xiàn)的方法


其中代碼中作者的注解可以看出框架具體的操作對象是 scrollView 而不是具體的 tableView 和 collectionView
其中如果該類是 scrollView 或者 scrollView 子類就對其添加監(jiān)聽。
其中isKindOfClass和isMemberOfClass的區(qū)別是需要關(guān)注的。

然后MJRefreshHeader和MJRefreshFooter都繼承MJRefreshComponent實(shí)現(xiàn)。
MJRefreshHeader
MJRefreshHeader繼承MJRefreshComponent,它做了這幾件事:
初始化。
設(shè)置header高度。
重新計(jì)算下拉高度值。
記錄上一次刷新時間或者是第一次刷新時間。
由contentOffset的變化,來切換狀態(tài)(默認(rèn)狀態(tài),可以刷新的狀態(tài),正在刷新的狀態(tài)),實(shí)現(xiàn)方法是:scrollViewContentOffsetDidChange:。
在切換狀態(tài)時,執(zhí)行相應(yīng)的操作。實(shí)現(xiàn)方法是:setState:。
兩種創(chuàng)建方式(兩種構(gòu)造方法)

重寫父類方法,設(shè)置Y值 ,同時設(shè)置上次刷新時間

狀態(tài)切換

重寫控件所有狀態(tài)的set方法,設(shè)置

MJRefreshStateHeader
這個類是MJRefreshHeader類的子類,它做了兩件事:
設(shè)置stateLabel和lastUpdatedTimeLabel的位置。
根據(jù)控件狀態(tài)的切換(默認(rèn)狀態(tài),正在刷新狀態(tài)),實(shí)現(xiàn)了這兩個label顯示的文字的切換。
這里設(shè)置文字的方法有點(diǎn)兒類似按鈕。將每一個狀態(tài)對應(yīng)的提示文字放入一個字典里面,key是狀態(tài)的NSNumber形式。


布局方法

重寫set方法,更新刷新時間

1.作者通過使用block來讓用戶自己定義日期現(xiàn)實(shí)的格式,如果用戶沒有自定義,就使用作者提供的默認(rèn)格式。
2.默認(rèn)格式的設(shè)置里,判斷了是否是今日,是否是今年的情況。由具體時間顯示label的文字是否顯示年,月,日。
MJRefreshNormalHeader
MJRefreshNormalHeader 繼承 MJRefreshStateHeader,它主要做了兩件事:
它在MJRefreshStateHeader上添加了_arrowView和loadingView。
布局了這兩個view并在Refresh控件的狀態(tài)切換的時候改變這兩個view的樣式。
作者同樣是通過重寫父類的三個方法實(shí)現(xiàn)子類自定義本類的控件。



到此為止,我們已經(jīng)從MJRefreshComponent到MJRefreshNormalHeader的實(shí)現(xiàn)過程看了一遍??梢钥闯?,作者將prepare,placeSubviews以及setState:方法作為基類的方法,讓下面的子類去一層一層實(shí)現(xiàn)。
而每一層的子類,根據(jù)自身的職責(zé),分別按照自己的方式來實(shí)現(xiàn)這三個方法:
MJRefreshHeader: 負(fù)責(zé)header的高度和調(diào)整header自身在外部的位置。
MJRefreshStateHeader:負(fù)責(zé)header內(nèi)部的stateLabel和lastUpdatedTimeLabel的布局和不同狀態(tài)下內(nèi)部文字的顯示。
MJRefreshNormalHeader:負(fù)責(zé)header內(nèi)部的loadingView以及arrowView的布局和不同狀態(tài)下的顯示。
這樣做的好處是,如果想要增加某種類型的header,只要在某一層上做文章即可。例如該框架里的MJRefreshGifHeader,它和MJRefreshNormalHeader屬于同一級,都是繼承于MJRefreshStateHeader。因?yàn)槎叨季哂邢嗤问降膕tateLabel和lastUpdatedTimeLabel,唯一不同的就是左側(cè)的部分:
MJRefreshNormalHeader的左側(cè)是箭頭。
MJRefreshGifHeader的左側(cè)則是一個gif動畫。
關(guān)于兩種footerView的區(qū)別。
BackFooter 自動彈回底部
backFooter 是上拉出現(xiàn)加載 view ,然后在加載完以后就這個 view 就自動彈回底部的。即使是 tableView 或者是 collectionView 當(dāng)數(shù)據(jù)量還不足以填充滿整個界面的時候也是如此的。
Auto Footer 自動刷新
自動刷新方式是沒有明顯的上拉的動作,就是當(dāng)滑動到最底部的時候,就自動加載。而當(dāng) tableView 或者 collectionView 的數(shù)據(jù)不足以填充滿這個屏幕的時候,刷新的 view 也是現(xiàn)實(shí)在 最后一個數(shù)據(jù)項(xiàng)的下面一項(xiàng)。然后點(diǎn)擊加載更多。
總體而言,這個框架是在最基類里面實(shí)現(xiàn)了最基本的流程,而每個流程怎么處理則因?yàn)?header 和 Footer 的不同需要不同的邏輯處理。所以有 header 和 Footer 的子類。
但是由于 Footer 的上拉有兩種樣式和情況,所以在 Footer 的基礎(chǔ)上又實(shí)現(xiàn)兩個類 backFooter 和 autoFooter 。