MJRefresh 中的 MJRefreshComponent , 面試官問我源代碼,iOS 吹風指南:(無大段代碼清爽版)

若是是一名 iOS 開發者, 用 Objective-C,設計模式

  • 平時用的 mj_header , mj_footer, 究竟是什麼?
  • 是怎麼給 mj_header , mj_footer, 添加方法的?

·····bash

平時給 tableView , collectionView 添加的 mj_footer , mj_header ,都是 MJRefreshComponent 的子類。網絡

下拉刷新的框架設計,先搞定基礎服務,就是 MJRefreshComponent 了。框架

MJRefreshComponent ,KVO + 狀態管理,造相似系統的代理方法

MJRefreshComponent 本質上是一個 UIView, 添加了一個父視圖對象 UIScrollView。而後須要把咱們用的 mj_footer , mj_header 添加到父視圖對象 scrollView 上面。 那個 scrollView 就是咱們業務代碼裏面的 tableView , collectionView .函數

要想實現咱們的下拉刷新,上拉加載, 就須要實現相關基礎設施。對用戶下拉和上拉做出反饋,MJRefreshComponent 採用的是觀察三個屬性。scrollView 的 contentOffset 和 contentSize ,scrollView 自帶的平移手勢 panGestureRecognizer 的 state.ui

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)contextatom

經過 KVO,把這三個屬性轉化爲三個方法,spa

// 偏移量改變
- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change{}

// 內容大小改變
- (void)scrollViewContentSizeDidChange:(NSDictionary *)change{}

// 拖拽狀態發生改變
- (void)scrollViewPanStateDidChange:(NSDictionary *)change{}

複製代碼

Header 只調用 scrollViewContentOffsetDidChange:, Footer 調用上面三個方法。 Header 只關注 偏移量, Footer 都要關注。設計

至關於 MJ 使用 KVO 生造了三個相似系統 scrollView 的代理方法,出來了。代理

MJRefresh 處理的至關有節操, 直接採用 KVO , 不涉及任何的 scrollView 的系統代理方法。 很是的框架。不打擾業務代碼的可能實現。

MJRefreshComponent 裏面有一個內部方法 - (void)executeRefreshingCallback , 處理咱們傳入的事件,業務上通常是網絡請求方法,上拉刷新第一頁的數據,下拉加載下一頁的數據。

能夠檢測到用戶的操做了, 就要做出響應,顯示 UI ,出現一朵菊花,執行咱們的業務代碼(做爲外部傳入給框架的)。

KVO 檢測到的 scrollView 的偏移量 contentOffset, 內容大小 contentSize 是不斷變化的,一觸發, 啪啪啪, 一打數據甩過去了。

咱們須要的是,在合適的時機,調用一次方法就行了。

MJRefreshComponent 採用的是狀態管理。

/** 刷新控件的狀態 */
typedef NS_ENUM(NSInteger, MJRefreshState) {
    MJRefreshStateIdle = 1,      /** 普通閒置狀態 */    
    MJRefreshStatePulling,       /** 鬆開就能夠進行刷新的狀態 */
    MJRefreshStateRefreshing,     /** 正在刷新中的狀態 */
    MJRefreshStateWillRefresh,    /** 即將刷新的狀態 */
    MJRefreshStateNoMoreData     /** 全部數據加載完畢,沒有更多的數據了 */
};

複製代碼

把監測到的 scrollView 的偏移量和內容大小的變化,轉化爲狀態的改變,把連續的數據變化轉化爲離散的一兩次操做,挺不錯的。具體在子類頭部刷新(下拉刷新)MJRefreshHeader 和尾巴刷新(上拉加載)MJRefreshFooter 中實現。

執行開發者的操做 (通常是網絡請求)

檢測到用戶的列表滾動狀況了,能夠轉化爲操做了,就要顯示UI, 放在 MJRefreshComponent 的子類頭部/尾部類中實現了。而後就是執行開發者傳入的方法。

這個內部方法 - (void)executeRefreshingCallback ,提供兩種實現,一種是匿名函數,self.refreshingBlock(); , 另外一種是 target - action, MJ 封裝了一個運行時的發消息方法 MJRefreshMsgSend(MJRefreshMsgTarget(self.refreshingTarget), self.refreshingAction, self);

繼承,MJRefreshComponent 做爲父類,提供基礎設施,Header 和 Footer 具體功能,更進一步的子類提供樣式 ( UI )

MJRefresh 在繼承上使用的比較出彩,MJRefreshComponent 創建基礎設施,MJRefreshHeader 完成了下拉加載的功能,MJRefreshStateHeader 添加了基本的樣式(主要是文本標籤 UILabel , 更新的時間文本, 狀態文本), MJRefreshNormalHeader 添加了箭頭和菊花(活動指示器,UIActivityIndicatorView )

具體上拉加載和下拉刷新的 UI 部分,MJRefreshComponent 提供了大體以下四個空方法實現。

/** 擺放子控件frame */
- (void)placeSubviews NS_REQUIRES_SUPER;

/** 當scrollView的contentOffset發生改變的時候調用 */
- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change NS_REQUIRES_SUPER;

/** 當scrollView的contentSize發生改變的時候調用 */
- (void)scrollViewContentSizeDidChange:(NSDictionary *)change NS_REQUIRES_SUPER;

/** 當scrollView的拖拽狀態發生改變的時候調用 */
- (void)scrollViewPanStateDidChange:(NSDictionary *)change NS_REQUIRES_SUPER;
複製代碼

MJRefresh 在繼承上有兩步幹得漂亮, 從 MJRefreshComponent 到 Header 和 Footer , 這裏有一個區分。

還有就是 MJMJRefresh 有不少的樣式,比較有特點的是上拉刷新部分, 會回彈到底部的默認的 footer , 會回彈到底部的帶動圖的 footer , 會自動刷新的默認的 footer ,會自動刷新的帶動圖的 footer.

內存管理上,

MJRefreshComponent 在內存管理上,也有優勢,

@interface MJRefreshComponent: UIView
{
    /** 父控件 */
    __weak UIScrollView * _scrollView;
}

/** 父控件 */
@property (weak, nonatomic, readonly) UIScrollView *scrollView;

複製代碼

父視圖 scrollView,咱們的表視圖,格子視圖,外部 readonly 屬性調用, 內部的 _scrollView 成員變量修改。

Runtime 上, 給系統類 scrollView 動態添加屬性

- (void)setMj_footer:(MJRefreshFooter *)mj_footer{
    if (mj_footer != self.mj_footer) {
        // 刪除舊的,添加新的
        [self.mj_footer removeFromSuperview];
        [self insertSubview:mj_footer atIndex:0];
        // 存儲新的
        objc_setAssociatedObject(self, &MJRefreshFooterKey, mj_footer, OBJC_ASSOCIATION_RETAIN);
    }
}


- (MJRefreshFooter *)mj_footer{
    return objc_getAssociatedObject(self, &MJRefreshFooterKey);
}


複製代碼

這一點上,NSHipster 的對象關聯 Associated Objects 講得很不錯。

End

MJRefresh 對設計模式中繼承, 觀察者的使用, 使用繼承的設計模式,分層加功能, 對 Runtime 的使用,對宏的大量使用, 都挺精彩的。

相關文章
相關標籤/搜索