如何优雅实现下拉刷新和上滑加载? —— 读 MJRefresh 源码

77 阅读1分钟
image.png

一句话回答

开始刷新: 通过KVO,监听UIScrollViewcontentOffset属性,拖动到达临界点时,切换刷新状态,执行刷新操作

添加监听代码
- (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];
    [NSNotificationCenter.defaultCenter addObserver:self selector: @selector(i18nDidChange) name:MJRefreshDidChangeLanguageNotification object:MJRefreshConfig.defaultConfig];
}

切换刷新状态,执行刷新操作 (MJRefreshHeader.m 文件中)

- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change
{
    ...
    } else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开
        // 开始刷新
        [self beginRefreshing];
    } 
    ...
}

- (void)executeRefreshingCallback {
    执行开发者一开始设置好的 block
    if (self.refreshingBlock) {
        self.refreshingBlock();
    }
    ...
}

停止刷新: 刷新成功后,手动调用 endRefreshing 函数,结束刷新动作,还原insetoffset

其他要知道的地方

默认情况下,MJRefresh 添加到 scrollview 之后,然后 scrollview 的起始 y 会设置为负数,这样在「不下拉」的时候就看不到,下拉后通过增大 contentInset,相当于把 scrollview 往下挤。刷新完成,再把 contentInset 设置回去,达到回弹的效果。

- (void)headerRefreshingAction {
    CGFloat top = self.scrollViewOriginalInset.top + self.mj_h;
    ...
    CGRect bounds = self.scrollView.bounds;
    bounds.origin.y = -top;
    通过动画设置起始 y
    [self.scrollView.layer addAnimation:boundsAnimation forKey:MJRefreshHeaderRefreshingBoundsKey];
}

参考资料

CSDN 参考资料:MJRefresh 原理分析