MJRefresh源碼地址:CoderMJLee/MJRefresh · GitHub?
一、基本結(jié)構(gòu)
github上有MJ詳細的分析
二睬罗、MJRefresh最基本的刷新代碼
1、MJRefreshNormalHeader一般應(yīng)用
__weak UITableView *tableView = self.tableView;
MJRefreshNormalHeader *normal = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
// 模擬延遲加載數(shù)據(jù)愕难,因此2秒后才調(diào)用(真實開發(fā)中个榕,可以移除這段gcd代碼)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 結(jié)束刷新
[tableView.header endRefreshing];
});
}];
// 下拉刷新
tableView.header = normal;
tableView默認(rèn)是沒有header屬性的,這里MJ使用分類(runtime的關(guān)聯(lián)對象)給UIScrollView添加了一個header和footer屬性属提;同時,給header和footer手動增加了KVO監(jiān)聽。
看代碼:
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
}
}
這里關(guān)聯(lián)對象使用OBJC_ASSOCIATION_ASSIGN斟薇,我認(rèn)為是和使用@property(nonmotic, weak)UIView *view; 中的weak一個意思的。放在view上的控件恕酸,不需要再次對其強引用堪滨。
- (MJRefreshHeader *)header
{
return objc_getAssociatedObject(self, &MJRefreshHeaderKey);
}
你只需要拖動tableView,或者手動調(diào)用beginRefreshing方法蕊温,均可以達到刷新的效果袱箱。
2、將MJRefreshNormalHeader對象賦值給header時執(zhí)行的操作
因為MJRefreshNormalHeader繼承MJRefreshComponent义矛,在MJRefreshComponent內(nèi)實例化過程中犯眠,先在initWithFrame方法中調(diào)用了prepare方法;
然后症革,在執(zhí)行addSubView時,在方法layoutSubviews中執(zhí)行了placeSubviews方法鸯旁,這幾簡要說明下layoutSubviews在什么時候會調(diào)用噪矛。
layoutSubviews是否被調(diào)用的情況:
1、init初始化不會觸發(fā)layoutSubviews铺罢。
2艇挨、addSubview會觸發(fā)layoutSubviews。
3韭赘、設(shè)置view的Frame會觸發(fā)layoutSubviews缩滨,當(dāng)然前提是frame的值設(shè)置前后發(fā)生了變化。
4泉瞻、滾動一個UIScrollView會觸發(fā)layoutSubviews脉漏。
5、旋轉(zhuǎn)Screen會觸發(fā)父UIView上的layoutSubviews事件袖牙。
6侧巨、改變一個UIView大小的時候也會觸發(fā)父UIView上的layoutSubviews事件。
疑問:為什么要額外準(zhǔn)備prepare和placeSubviews給子類調(diào)用鞭达,系統(tǒng)提供的方法完全夠用了司忱。
其次,調(diào)用willMoveToSuperview方法畴蹭,執(zhí)行一些基本的操作坦仍,添加了對scrollView的幾個參數(shù)KVO監(jiān)聽;代碼如下
- (void)willMoveToSuperview:(UIView *)newSuperview
{
[super willMoveToSuperview:newSuperview];
// 如果不是UIScrollView叨襟,不做任何事情
if (newSuperview && ![newSuperview isKindOfClass:[UIScrollView class]]) return;
// 舊的父控件移除監(jiān)聽
[self removeObservers];
if (newSuperview) { // 新的父控件
// 設(shè)置寬度
self.mj_w = newSuperview.mj_w;
// 設(shè)置位置
self.mj_x = 0;
// 記錄UIScrollView
_scrollView = (UIScrollView *)newSuperview;
// 設(shè)置永遠支持垂直彈簧效果
_scrollView.alwaysBounceVertical = YES;
// 記錄UIScrollView最開始的contentInset
_scrollViewOriginalInset = _scrollView.contentInset;
// 添加監(jiān)聽
[self addObservers];
}
}
三繁扎、往下拉tableView(包括其它UIScrollView)可以出現(xiàn)效果,并且在拉伸一定程度放手之后糊闽,能實現(xiàn)刷新的效果
1锻离、在MJRefreshComponent內(nèi)部實現(xiàn)了對scrollView的contentOffset铺峭、contentSize、state的KVO監(jiān)聽汽纠,見代碼:
#pragma mark - KVO監(jiān)聽
- (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卫键、對這三個值的監(jiān)聽,同時執(zhí)行以下相應(yīng)的方法
- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change{}
- (void)scrollViewContentSizeDidChange:(NSDictionary *)change{}
- (void)scrollViewPanStateDidChange:(NSDictionary *)change{}
這三個方法在MJRefreshComponent內(nèi)均沒有具體的實現(xiàn)虱朵,作用是為了交給子類實現(xiàn)莉炉。