美文网首页iOSios iOS第三方库
我对MJRefresh框架的了解 -> MJRefresh

我对MJRefresh框架的了解 -> MJRefresh

作者: shareMind | 来源:发表于2015-09-24 10:51 被阅读7136次

    正如源码中注释的一样,这个类的作用是:负责监控用户下拉的状态;

    pragma mark - 一、在.h文件中,提供了两种类方法实例化对象,分别是带block回调和target响应的方法,用户可根据自身习惯去选择,达到的目的都是相同的。

    • (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock
      {
      MJRefreshHeader *cmp = [[self alloc] init];
      cmp.refreshingBlock = refreshingBlock;
      return cmp;
      }
    • (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action
      {
      MJRefreshHeader *cmp = [[self alloc] init];
      [cmp setRefreshingTarget:target refreshingAction:action];
      return cmp;
      }
      不管是refreshingBlock,还是target和action,在执行beginRefreshing方法之后都有机会回调MJRefreshComponent中的executeRefreshingCallback方法。在executeRefreshingCallback中,都会去判断以及执行回调方法。

    pragma mark - 二、MJRefreshHeader通过重写父类的prepare和placeSubviews方法,来做一些基本的设置,代码如下

    • (void)prepare
      {
      [super prepare];
      // 设置key
      self.lastUpdatedTimeKey = MJRefreshHeaderLastUpdatedTimeKey;
      // 设置高度
      self.mj_h = MJRefreshHeaderHeight;//这里默认设置为54
      }
    • (void)placeSubviews
      {
      [super placeSubviews];
      // 设置y值(当自己的高度发生改变了,肯定要重新调整Y值,所以放到placeSubviews方法中设置y值)
      self.mj_y = - self.mj_h - self.ignoredScrollViewContentInsetTop;
      }

    pragma mark - 三、MJRefreshHeader的主要作用

    1、重写父类scrollViewContentOffsetDidChange方法,以达到监听scrollView的contentOffset发生变化的目的,并做一些实际的操作。
    需要指出的地方有:
    a、首先检测self.state的状态是否处于正在刷新的状态(也就是否等于枚举MJRefreshStateRefreshing),如果在刷新,那么直接结束,不做任何操作。

    b、CGFloat happenOffsetY = - self.scrollViewOriginalInset.top;
    疑问:为什么在UITableViewController管理的UITableView (这里我暂时只试了这个,其它情况不清楚)状态下,这个值一直是64,不管怎么上下拖动tableView都不会发生变化。

    c、CGFloat offsetY = self.scrollView.mj_offsetY;
    疑问:在tableView启动默认状态下,这个值为-64,往上移动时,这个值会变大; 往下移动时,这个值会变小;但其它一般的scrollView,确不是这么变化的。(我没怎么认真研究过tableView的contentOffset的变化情况,涨姿势了)

    d、当偏移量(也就是offsetY)值变大时,只要大于happenOffsetY(操作时,这个值至始至终是-64),该方法就直接结束。

    e、CGFloat normal2pullingOffsetY = happenOffsetY - self.mj_h;
    设置了一个即将刷新的临界值,因为happenOffsetY至始至终为-64,self.mj_h在prepare方法中设置为54,所以这个临界值为-118 。

    f、 CGFloat pullingPercent = (happenOffsetY - offsetY) / self.mj_h;
    计算拉伸的比例,当offsetY为-64时,也就是tableView启动默认状态下的情况,pullingPercent值是为0的;
    往上拖动时,由于offsetY变大,pullingPercent会为负数;
    往下拖动时,offsetY变小,当offsetY在-64和-118中间时,pullingPercent是一个小数值。 当offsetY小于-118时,pullingPercent就会大于1 。

    g、self.scrollView.isDragging判断该scrollView(就是MJRefreshComponent的父类)是否在拖动状态。
    源码:if (self.scrollView.isDragging) { // 如果正在拖拽
    NSLog(@"isDragging");
    self.pullingPercent = pullingPercent;
    if (self.state == MJRefreshStateIdle && offsetY < normal2pullingOffsetY) {
    // 转为即将刷新状态
    self.state = MJRefreshStatePulling;
    } else if (self.state == MJRefreshStatePulling && offsetY >= normal2pullingOffsetY) {
    // 转为普通状态
    self.state = MJRefreshStateIdle;
    }
    }
    else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开
    // 开始刷新
    [self beginRefreshing];
    }
    else if (pullingPercent < 1) {
    self.pullingPercent = pullingPercent;
    }

    为真,在拖动状态:
    如果偏移量offsetY值小于(注意往下拉是负数)临界点normal2pullingOffsetY(也就是-118)时,同时这个时候的self.state等于空闲状态时(也就是MJRefreshStateIdle),那么将state置位刷新状态(也就是MJRefreshStatePulling);
    就这样,当该scrollView在拖动的时候,self.state来回在MJRefreshStatePulling 和 MJRefreshStateIdle之间切换,并且相应的执行self.state的setter方法(后面会对setter方法有进一步分析);

    为假,放手了,不在拖动状态:
    如果self.state等于MJRefreshStatePulling状态,放手就开始执行beginRefreshing方法。

    源码:scrollViewContentOffsetDidChange方法代码如下:

    屏幕快照 2015-09-24 下午4.17.11.png

    2、MJRefreshComponent的beginRefreshing方法,代码如下:

    • (void)beginRefreshing
      {
      [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
      self.alpha = 1.0;
      }];
      self.pullingPercent = 1.0;
      // 只要正在刷新,就完全显示
      NSLog(@"self.window->%@",self.window);
      if (self.window) {
      self.state = MJRefreshStateRefreshing;
      } else {
      NSLog(@"else /self.window->%@",self.window);
      self.state = MJRefreshStateWillRefresh;
      // 刷新(预防从另一个控制器回到这个控制器的情况,回来要重新刷新一下)
      [self setNeedsDisplay];
      }
      }
      在前文提到的放手就开始执行beginRefreshing方法,内部就再次对self.state进行MJRefreshStateRefreshing赋值,执行self.state的setter方法。
      疑问:为什么在刷新的时候跳转到另外一个控制器self.window会成为空,而且还会第二次执行beginRefreshing方法,从而将self.state赋值为MJRefreshStateWillRefresh。

    3、根据scrollViewContentOffsetDidChange内部执行的操作,来设置MJRefreshState的状态。MJRefreshHeader内部有重写父类中state的setter方法,

    a、注意点这两句代码:MJRefreshState oldState = self.state; if (state == oldState) return;
    一开始我没理解这个逻辑,后来一想,如果在前面加一段_state = state;那么这就是我最初理解的了。只能说MJ让我涨姿势了~~~

    b、下拉刷新时,当执行了beginRefreshing方法,内部将self.state设置为MJRefreshStateRefreshing,并且调用self.state的setter方法,这个时候执行这串代码:
    [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
    // 增加滚动区域
    CGFloat top = self.scrollViewOriginalInset.top + self.mj_h;
    self.scrollView.mj_insetT = top;//top值将会为118
    // 设置滚动位置
    self.scrollView.mj_offsetY = - top;
    } completion:^(BOOL finished) {
    [self executeRefreshingCallback];
    }];
    这里修改UIScrollView的contentInset属性,通过增加UIScrollView额外的滚动区域来达到显示的效果。
    同时,将contentOffset的Y值设置为-118;

    c、当下拉刷新完成时,假如执行了endRefreshing操作,内部会通过setter方法将state置位MJRefreshStateIdle,这个时候就会执行这串代码:
    // 保存刷新时间
    [[NSUserDefaults standardUserDefaults] setObject: [NSDate date] forKey:self.lastUpdatedTimeKey];
    [[NSUserDefaults standardUserDefaults] synchronize];
    // 恢复inset和offset
    [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{
    self.scrollView.mj_insetT -= self.mj_h;
    // 自动调整透明度
    if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0;
    } completion:^(BOOL finished) {
    self.pullingPercent = 0.0;
    }];
    重新将UIScrollView的contentInset属性设置位最初的值(对于一般状态的UITableView也就是64);并且保存了最后结束刷新的时间 和 self的透明度。

    源代码如下:

    屏幕快照 2015-09-24 下午4.16.37.png

    4、其它
    a、对象调用方法永远都是从自己的方法列表中去寻找,当找不到的时候,才会去父类寻找方法。MJ很好的灵活运用了这个机制;

    b、@property ( nonatomic ) UIEdgeInsets contentInset; 这个属性能够在UIScrollView的4周增加额外的滚动区域 ;

    c、MJRefreshState oldState = self.state; if (state == oldState) return;之所以能拿到之前的state状态,通过state的getter方法来获取,这个时候属性_state并没有被更改;如果在这两句代码之前加上_state = state , 那么情况将会完全不一样;

    d、如果自己去打断点执行一遍,思路会更清晰;

    相关文章

      网友评论

        本文标题:我对MJRefresh框架的了解 -> MJRefresh

        本文链接:https://www.haomeiwen.com/subject/oinicttx.html