每次讀優秀的代碼都是一次深入的學習,每一次模仿,都是創造的開始!編程
——QQ 316045346 歡迎交流安全
MJRefresh主要爲UIScrollView,UITableView和UICollectionView添加頭部和尾部刷新控件。其主要由3大塊組成,類別工具,核心UIScrollView類別和頭部尾部刷新組件。以下圖:框架
上面示意圖中列出的幾個工具類別主要提供方便屬性訪問的功能。其主要是爲了方便MJRefresh庫本身的調用,固然你也能夠對它進行使用。函數
UIView+MJExtension類別提供了對UIView組件位置和尺寸的快速訪問方法,而且都支持快速獲取和設置:工具
@property (assign, nonatomic) CGFloat mj_x; @property (assign, nonatomic) CGFloat mj_y; @property (assign, nonatomic) CGFloat mj_w; @property (assign, nonatomic) CGFloat mj_h; @property (assign, nonatomic) CGSize mj_size; @property (assign, nonatomic) CGPoint mj_origin;
UIScrollView+MJExtension提供了對UIScrollView的內容尺寸,偏移量等屬性的快速訪問:源碼分析
@property (readonly, nonatomic) UIEdgeInsets mj_inset; @property (assign, nonatomic) CGFloat mj_insetT; @property (assign, nonatomic) CGFloat mj_insetB; @property (assign, nonatomic) CGFloat mj_insetL; @property (assign, nonatomic) CGFloat mj_insetR; @property (assign, nonatomic) CGFloat mj_offsetX; @property (assign, nonatomic) CGFloat mj_offsetY; @property (assign, nonatomic) CGFloat mj_contentW; @property (assign, nonatomic) CGFloat mj_contentH;
NSBundle+MJRefresh這個類別提供對庫中資源的訪問方法:佈局
+ (instancetype)mj_refreshBundle; //獲取箭頭圖片 + (UIImage *)mj_arrowImage; //獲取國際化字符串 + (NSString *)mj_localizedStringForKey:(NSString *)key value:(NSString *)value; + (NSString *)mj_localizedStringForKey:(NSString *)key;
這個類別是MJRefresh庫的核心,其中提供的mj_header和mj_footer兩個屬性用來添加頭部和尾部刷新組件。這兩個組件是做爲子視圖添加在UIScrollView上的,所以和UIScrollView的原生頭尾視圖都不影響。在之前版本的MJRefresh中,使用的是header和footer屬性,容易產生疑惑,所以後面版本框架中都添加了mj前綴。學習
UIScrollView+MJRefresh類別在開發者設置mj_header和mj_footer屬性時,將這兩個組件添加爲當前滾動視圖的最下層子視圖,爲了知足某些自動加載的需求,這裏面有用runtime將UITableView和UICollectionView的reload函數進行替換,這樣作的目的是爲了在數據加載時統計界面的元素個數。動畫
MJRefreshComponent是刷新組件的基類,其中定了一些通用方法。首先,MJRefresh庫的刷新組件核心思想是基於狀態的,即經過狀態來觸發某些組件行爲,例如正常的常態,下拉的pulling態,釋放的refreshing態等等。開發者除了能夠手動設置狀態外,主要經過監聽UIScrollView的偏移量等屬性來改變狀態。當UIScrollView有偏移量或內容尺寸的變化時,MJRefreshComponent會調用scrollViewContentOffsetDidChange函數,這個函數主要交給其子類實現。atom
MJRefreshHeader類是頭部刷新組件的基類,其將刷新組件佈局在UIScrollView組件的頂部,而且封裝了記錄上次刷新時間的功能。MJRefreshStateHeader提供了接口供開發者設置不一樣狀態下刷新組件所顯示的文字,MJRefreshNormalHeader是一個更加上層的頭部刷新組件,其狀態文字是默認定義好的,而且支持國際化。MJRefreshGifHeader能夠支持顯示自定義刷新動畫,其能夠爲某個狀態設置一組圖片。
尾部刷新組件的編寫邏輯和頭部刷新組件的編寫邏輯基本一致,MJRefresh中的尾部刷新組件分爲了兩類,一類是刷新完成後自動消失的,一類是自動刷新,刷新完成後不會自動消失,只是改變狀態。MFRefreshFooter與MJRefreshHeader的實現基本一致,MJRefreshBackFooter有刷新完成後自動還原的功能,MJRefreshBackNormalFooter是比較上層的封裝,其顯示默認的狀態文案,而且支持國際化。MJRefreshBackStateFooter則能夠手動設置不一樣狀態下刷新組件顯示的文字。MJRefreshBackGifFooter用來顯示自定義動畫的尾部刷新組件。MJRefreshAutoFooter是自動尾部刷新組件的基類,其能夠設置當尾部刷新組件出現多少比例時進行刷新(默認是徹底出現後進行刷新)。MJRefreshAutoStateFooter能夠自定義其各個狀態的文案。一樣,也有比較上層的MJRefreshAotuNormalFooter組件,這個組件封裝好了國際化的文案能夠直接使用,MJRefreshAutoGifFooter組件能夠顯示自定動畫的尾部刷新。
之因此看MJRefresh庫的代碼很是舒服,很大一部分源自其深刻的複用。首先MJRefreshComponent類抽象出了回調與刷新函數,而且提取出了須要子類複寫的通用的佈局、監聽等函數,讓子類的結構很是統一。MJRefreshHeader和MJRefreshFooter做爲頭部與尾部刷新組件的基類,抽象出了構造函數,而且實現了大部分組件與外部的佈局,邏輯動做等函數。再子類則專一與實現子類自身的UI與功能。還有一個小細節,也能夠看出MJRefresh對複用的追求,在setState函數的實現中,若是新的狀態與舊的狀態一致,則不須要作任何邏輯,全部的setState函數都須要這個邏輯,MJRefresh中採用的宏的方式進行替換,使代碼變得十分簡潔,示例以下:
#define MJRefreshCheckState \ MJRefreshState oldState = self.state; \ if (state == oldState) return; \ [super setState:state]; /////////// - (void)setState:(MJRefreshState)state { MJRefreshCheckState // 設置狀態文字 self.stateLabel.text = self.stateTitles[@(state)]; }
不少時候,咱們在執行block的時候都會先檢查下這個block是否爲nil,下面是咱們經常使用的代碼:
if (block) { block(); }
在MJRefresh中有使用問號冒號的方式來代替if語句,以下:
- (void)executeReloadDataBlock { ! self.mj_reloadDataBlock ? : self.mj_reloadDataBlock(self.mj_totalDataCount); }
這個表達式初看會有一些疑惑,其實?:的做用是返回一個值,若是?:前的表達式爲nil的話,則會返回?:後面的值,一樣,若是?:前面的表達式不爲nil的話,則直接返回,不會執行到後面的表達式,上面的寫法其實和第一種if語句的做用徹底一致。