首先看看MJRefrsh.h
#import"UIScrollView+MJRefresh.h"
#import"UIScrollView+MJExtension.h"
一般都是利用UIScrollView的偏移量来判断刷新的, 在UIScrollView+MJRefresh.h中采用动态添加属性的方法objc_setAssociatedObject()和objc_getAssociatedObject()函数给UIScrollView动态添加属性MJRefreshHeader*header和MJRefreshFooter*footer;
一般情况下category里是不能添加属性的,但是可用通过采用动态添加属性的方法objc_setAssociatedObject()和objc_getAssociatedObject()函数给UIScrollView动态添加属性
这里添加了来那个属性,看看.m文件里的内容
这样就能够属性与UIScrollView关联
我们自己在给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 等构造方法,在MJRefreshHeader的父类中MJRefreshComponent中有相应
的构造方法
MJRefreshComponent类是MJ这个第三方库的一个基类,其他的header和footer都是继承这个类
- (void)prepare 这里MJRefreshComponent实现的是控件的公共属性,而一些特有的属性在每个子类里实现
到这里就完成的UIView的生成,但是不确定他的大小和位置, 尽管有回调的函数,
_resuleTable.header 点语法条用了动态给UIScrollerView的属性的getter 和setter方法
这里有 addSubview 方法的调用,接下来就会进入header的生命周期方法willMoveToSuperview,这个方法是在公共的基类MJRefreshComponent里实现的。因为这是基础的行为,所以写在公共的基类里,所有的子类都能共享:
在这个方法中设置了监听,监听UIScrollView的偏移量,
//设置永远支持垂直弹簧效果
_scrollView.alwaysBounceVertical=YES; 保证了UIScrollView一只可以拉动。
#pragma mark - KVO监听
- (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{}
这里设置了三个监听,用于监听contentOffset、contentSize和state 当值变化的时候在调用三个方法。
NSString*constMJRefreshKeyPathContentOffset =@"contentOffset";
NSString*constMJRefreshKeyPathContentInset =@"contentInset";
NSString*constMJRefreshKeyPathContentSize =@"contentSize";
NSString*constMJRefreshKeyPathPanState =@"state";
接下来会进入生命周期方法layoutSubviews:
- (void)layoutSubviews
{
[superlayoutSubviews];
[selfplaceSubviews];
}
- (void)placeSubviews{}
placeSubviews具体有子类实现。例如在MJRefreshHeader中得实现和
- (void)placeSubviews
{
[superplaceSubviews];
//设置y值(当自己的高度发生改变了,肯定要重新调整Y值,所以放到placeSubviews方法中设置y值)
self.mj_y= -self.mj_h-self.ignoredScrollViewContentInsetTop;
}
在MJRefreshNormalHeader中的具体实现:
- (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,从而触发
- (void)scrollViewContentOffsetDidChange:(NSDictionary*)change{}方法。