我对MJRefresh框架的了解 -> MJRefreshComponent

MJRefresh源码地址:CoderMJLee/MJRefresh · GitHub 

一、基本结构


github上有MJ详细的分析

我对MJRefresh框架的了解 -> MJRefreshComponent_第1张图片

二、MJRefresh最基本的刷新代码

1、MJRefreshNormalHeader一般应用

__weak UITableView *tableView = self.tableView;

MJRefreshNormalHeader *normal = [MJRefreshNormalHeader headerWithRefreshingBlock:^{

// 模拟延迟加载数据,因此2秒后才调用(真实开发中,可以移除这段gcd代码)

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

// 结束刷新

[tableView.header endRefreshing];

});

}];

// 下拉刷新

tableView.header = normal;

tableView默认是没有header属性的,这里MJ使用分类(runtime的关联对象)给UIScrollView添加了一个header和footer属性;同时,给header和footer手动增加了KVO监听。

看代码:

static const char MJRefreshHeaderKey = '\0';

- (void)setHeader:(MJRefreshHeader *)header

{

if (header != self.header) {

// 删除旧的,添加新的

[self.header removeFromSuperview];

[self addSubview:header];

// 存储新的

[self willChangeValueForKey:@"header"]; // KVO

objc_setAssociatedObject(self, &MJRefreshHeaderKey,

header, OBJC_ASSOCIATION_ASSIGN);

[self didChangeValueForKey:@"header"]; // KVO

}

}

这里关联对象使用OBJC_ASSOCIATION_ASSIGN,我认为是和使用@property(nonmotic, weak)UIView *view; 中的weak一个意思的。放在view上的控件,不需要再次对其强引用。

- (MJRefreshHeader *)header

{

return objc_getAssociatedObject(self, &MJRefreshHeaderKey);

}

你只需要拖动tableView,或者手动调用beginRefreshing方法,均可以达到刷新的效果。

2、将MJRefreshNormalHeader对象赋值给header时执行的操作

因为MJRefreshNormalHeader继承MJRefreshComponent,在MJRefreshComponent内实例化过程中,先在initWithFrame方法中调用了prepare方法;

然后,在执行addSubView时,在方法layoutSubviews中执行了placeSubviews方法,这几简要说明下layoutSubviews在什么时候会调用。

layoutSubviews是否被调用的情况:

1、init初始化不会触发layoutSubviews。

2、addSubview会触发layoutSubviews。

3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化。

4、滚动一个UIScrollView会触发layoutSubviews。

5、旋转Screen会触发父UIView上的layoutSubviews事件。

6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件。

疑问:为什么要额外准备prepare和placeSubviews给子类调用,系统提供的方法完全够用了。

其次,调用willMoveToSuperview方法,执行一些基本的操作,添加了对scrollView的几个参数KVO监听;代码如下

- (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];

}

}



三、往下拉tableView(包括其它UIScrollView)可以出现效果,并且在拉伸一定程度放手之后,能实现刷新的效果

1、在MJRefreshComponent内部实现了对scrollView的contentOffset、contentSize、state的KVO监听,见代码:

#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];

}

2、对这三个值的监听,同时执行以下相应的方法

- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change{}

- (void)scrollViewContentSizeDidChange:(NSDictionary *)change{}

- (void)scrollViewPanStateDidChange:(NSDictionary *)change{}

这三个方法在MJRefreshComponent内均没有具体的实现,作用是为了交给子类实现。

你可能感兴趣的:(我对MJRefresh框架的了解 -> MJRefreshComponent)