今天终于有空揭开了MJRefresh源码的神秘面纱
结构:
1,MJRefreshComponent继承UIView,是所有刷新控件的基类。
2,MJRefreshHeader继承MJRefreshComponent,是所有头部刷新的基类。
3,MJRefreshFooter继承MJRefreshComponent,是所有尾部刷新的基类。尾部刷新分为auto和back2种方式,MJRefreshAutoFooter,MJRefreshBackFooter均继承MJRefreshFooter。
MJRefresh实现原理:
1,MJRefreshHeader / MJRefreshFooter添加到ScrollView
- (void)setMj_header:(MJRefreshHeader *)mj_header
{
if (mj_header != self.mj_header) {
// 删除旧的,添加新的
[self.mj_header removeFromSuperview];
[self insertSubview:mj_header atIndex:0];
// 存储新的
[self willChangeValueForKey:@"mj_header"]; // KVO
objc_setAssociatedObject(self, &MJRefreshHeaderKey,
mj_header, OBJC_ASSOCIATION_ASSIGN);
[self didChangeValueForKey:@"mj_header"]; // KVO
}
}
设置并获取父类scrollView相应属性(MJRefreshComponent)
- (void)willMoveToSuperview:(UIView *)newSuperview
{
[super willMoveToSuperview:newSuperview];
// 如果不是UIScrollView,不做任何事情
if (newSuperview && ![newSuperview isKindOfClass:[UIScrollView class]]) return;
// 旧的父控件移除监听
[self removeObservers];
if (newSuperview) { // 新的父控件
// 设置宽度
self.mj_w = newSuperview.mj_w;
// 设置位置
self.mj_x = 0;
// 记录UIScrollView
_scrollView = (UIScrollView *)newSuperview;
// 设置永远支持垂直弹簧效果
_scrollView.alwaysBounceVertical = YES;
// 记录UIScrollView最开始的contentInset
_scrollViewOriginalInset = _scrollView.contentInset;
// 添加监听
[self addObservers];
}
}
2,利用KVO监听UISrollView的ContentOffset,ContentSize,panGestureRecognizer的变化作相应的状态处理。(重点,核心)
关键代码:(MJRefreshComponent)
#pragma mark - KVO监听
- (void)addObservers
{
NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
[self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentOffset options:options context:nil];
[self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentSize options:options context:nil];
self.pan = self.scrollView.panGestureRecognizer;
[self.pan addObserver:self forKeyPath:MJRefreshKeyPathPanState options:options context:nil];
}
这些属性一但变化到一定值就调用相应的方法处理,比如头部下拉刷新:
- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change
{
[super scrollViewContentOffsetDidChange:change];
// 偏移量一旦达到一定值就调用相应方法
......
}
3,运行时
设置/获取 header/footer
// 存储
[self willChangeValueForKey:@"mj_header"]; // KVO
objc_setAssociatedObject(self, &MJRefreshHeaderKey,
mj_header, OBJC_ASSOCIATION_ASSIGN);
[self didChangeValueForKey:@"mj_header"]; // KVO
// 获取
return objc_getAssociatedObject(self, &MJRefreshHeaderKey);
执行taget 的selector
#pragma mark - 内部方法
- (void)executeRefreshingCallback
{
dispatch_async(dispatch_get_main_queue(), ^{
if (self.refreshingBlock) {
self.refreshingBlock();
}
if ([self.refreshingTarget respondsToSelector:self.refreshingAction]) {
MJRefreshMsgSend(MJRefreshMsgTarget(self.refreshingTarget), self.refreshingAction, self);
}
if (self.beginRefreshingCompletionBlock) {
self.beginRefreshingCompletionBlock();
}
});
}
4,补充重写
自己重写继承相应的父类,主要重写以下3个方法就OK了
#pragma mark - 覆盖父类的方法
// 准备初始化工作,如添加不同状态的文字显示
- (void)prepare
{
[super prepare];
}
// 布局自定的控件
- (void)placeSubviews
{
[super placeSubviews];
}
// 不同状态的处理
- (void)setState:(MJRefreshState)state
{
MJRefreshCheckState
}
网友评论