首先看看MJRefrsh.h

#import"UIScrollView+MJRefresh.h"
#import"UIScrollView+MJExtension.h"
一般都是利用UIScrollView的偏移量來判斷刷新的, 在UIScrollView+MJRefresh.h中采用動態(tài)添加屬性的方法objc_setAssociatedObject()和objc_getAssociatedObject()函數(shù)給UIScrollView動態(tài)添加屬性MJRefreshHeader*header和MJRefreshFooter*footer;
一般情況下category里是不能添加屬性的,但是可用通過采用動態(tài)添加屬性的方法objc_setAssociatedObject()和objc_getAssociatedObject()函數(shù)給UIScrollView動態(tài)添加屬性

這里添加了來那個屬性,看看.m文件里的內(nèi)容

這樣就能夠?qū)傩耘cUIScrollView關(guān)聯(lián)
我們自己在給tableView添加上拉下拉刷新的時候需要為tableView添加header和footer
已給tableView添加MJRefreshNormalHeader類型的header為例:
_resuleTable.header= [MJRefreshNormalHeaderheaderWithRefreshingTarget:selfrefreshingAction:@selector(headerRefresh)];
MJRefreshNormalHeader里面沒有下面這個方法,但是他的父類MJRefreshHeader有這個方法。
+ (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action
+ (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action{
MJRefreshHeader*cmp = [[selfalloc]init];
[cmpsetRefreshingTarget:targetrefreshingAction:action];
returncmp;
}
然而在MJRefreshHeader并沒有-init 等構(gòu)造方法,在MJRefreshHeader的父類中MJRefreshComponent中有相應(yīng)
的構(gòu)造方法

MJRefreshComponent類是MJ這個第三方庫的一個基類,其他的header和footer都是繼承這個類
- (void)prepare?這里MJRefreshComponent實現(xiàn)的是控件的公共屬性,而一些特有的屬性在每個子類里實現(xiàn)
到這里就完成的UIView的生成,但是不確定他的大小和位置, ?盡管有回調(diào)的函數(shù),
_resuleTable.header 點語法條用了動態(tài)給UIScrollerView的屬性的getter 和setter方法
這里有 addSubview 方法的調(diào)用,接下來就會進入header的生命周期方法willMoveToSuperview,這個方法是在公共的基類MJRefreshComponent里實現(xiàn)的。因為這是基礎(chǔ)的行為,所以寫在公共的基類里,所有的子類都能共享:

在這個方法中設(shè)置了監(jiān)聽,監(jiān)聽UIScrollView的偏移量,
//設(shè)置永遠支持垂直彈簧效果
_scrollView.alwaysBounceVertical=YES; 保證了UIScrollView一只可以拉動。
#pragma mark - KVO監(jiān)聽
- (void)addObservers
{
NSKeyValueObservingOptionsoptions =NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld;
[self.scrollViewaddObserver:selfforKeyPath:MJRefreshKeyPathContentOffsetoptions:optionscontext:nil];
[self.scrollViewaddObserver:selfforKeyPath:MJRefreshKeyPathContentSizeoptions:optionscontext:nil];
self.pan=self.scrollView.panGestureRecognizer;
[self.panaddObserver:selfforKeyPath:MJRefreshKeyPathPanStateoptions:optionscontext:nil];
}
- (void)removeObservers
{
[self.superviewremoveObserver:selfforKeyPath:MJRefreshKeyPathContentOffset];
[self.superviewremoveObserver:selfforKeyPath:MJRefreshKeyPathContentSize];;
[self.panremoveObserver:selfforKeyPath:MJRefreshKeyPathPanState];
self.pan=nil;
}
- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context
{
//遇到這些情況就直接返回
if(!self.userInteractionEnabled)return;
//這個就算看不見也需要處理
if([keyPathisEqualToString:MJRefreshKeyPathContentSize]) {
[selfscrollViewContentSizeDidChange:change];
}
//看不見
if(self.hidden)return;
if([keyPathisEqualToString:MJRefreshKeyPathContentOffset]) {
[selfscrollViewContentOffsetDidChange:change];
}elseif([keyPathisEqualToString:MJRefreshKeyPathPanState]) {
[selfscrollViewPanStateDidChange:change];
}
}
- (void)scrollViewContentOffsetDidChange:(NSDictionary*)change{}
- (void)scrollViewContentSizeDidChange:(NSDictionary*)change{}
- (void)scrollViewPanStateDidChange:(NSDictionary*)change{}
這里設(shè)置了三個監(jiān)聽,用于監(jiān)聽contentOffset、contentSize和state ?當值變化的時候在調(diào)用三個方法。
NSString*constMJRefreshKeyPathContentOffset =@"contentOffset";
NSString*constMJRefreshKeyPathContentInset =@"contentInset";
NSString*constMJRefreshKeyPathContentSize =@"contentSize";
NSString*constMJRefreshKeyPathPanState =@"state";
接下來會進入生命周期方法layoutSubviews:
- (void)layoutSubviews
{
[superlayoutSubviews];
[selfplaceSubviews];
}
- (void)placeSubviews{}
placeSubviews具體有子類實現(xiàn)。例如在MJRefreshHeader中得實現(xiàn)和
- (void)placeSubviews
{
[superplaceSubviews];
//設(shè)置y值(當自己的高度發(fā)生改變了,肯定要重新調(diào)整Y值,所以放到placeSubviews方法中設(shè)置y值)
self.mj_y= -self.mj_h-self.ignoredScrollViewContentInsetTop;
}
在MJRefreshNormalHeader中的具體實現(xiàn):
- (void)placeSubviews
{
[superplaceSubviews];
//箭頭
self.arrowView.mj_size=self.arrowView.image.size;
CGFloatarrowCenterX =self.mj_w*0.5;
if(!self.stateLabel.hidden) {
arrowCenterX -=100;
}
CGFloatarrowCenterY =self.mj_h*0.5;
self.arrowView.center=CGPointMake(arrowCenterX, arrowCenterY);
//圈圈
self.loadingView.frame=self.arrowView.frame;
}
實際下拉的時候會改變ContentOffset,從而觸發(fā)
- (void)scrollViewContentOffsetDidChange:(NSDictionary*)change{}方法。