美文网首页收藏iosDevSupport
iOS 下拉刷新实现原理

iOS 下拉刷新实现原理

作者: ThinkerH | 来源:发表于2017-01-06 21:38 被阅读2625次

最近研究iOS的下拉刷新,想自己封装个下拉刷新控件,所以首先研究了下实现的原理。

这里主要涉及到两个知识点,如下:

1.KVO

2.UIScrollView的下面三个属性

@property(nonatomic)         CGPoint                      contentOffset;                  // default CGPointZero
@property(nonatomic)         CGSize                       contentSize;                    // default CGSizeZero
@property(nonatomic)         UIEdgeInsets                 contentInset;                   // default UIEdgeInsetsZero. add 

1.KVO

KVO是iOS中观察者模式的一种实现方式,具体实现机制这里不赘述,可参考这篇文章《谈谈 KVO》
下拉刷新控件要实现首先就要监控UIScrollView(UITableVeiw继承自UIScrollView)contentOffset属性值,这里需要定义一个UIScrollView的扩展类(catagray),在扩展类的实例方法中通过KVO监控UIScrollView下拉是的contentOffset属性值变化,具体代码如下:

@implementation UIScrollView (RefreshView)

- (void)addRefreshWithRefreshViewType:(HLRefreshViewType)refreshViewType refreshingBlock:(void (^)())block
{
   HLRefresh *refresh = [HLRefresh refreshWithRefreshViewType:refreshViewType refreshingBlock:block];
    refresh.scrollView = self;
    
    [self addObserver:refresh forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
    [self addSubview:refresh];
}
//这里的`HLRefresh`是下拉刷新时显示刷新状态的头View

然后在KVO的Observer——refresh对象的HLRefresh类中实现如下:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    HLOG(@"=============")
    //如果当前状态是 HLRefreshHeaderStateRefreshing 不再做检测处理
    if (_headerState ==  HLRefreshHeaderStateRefreshing) {
        return;
    }
    [self adjustStateWithContentOffset];
}

- (void)adjustStateWithContentOffset
{
    //记录 scrollView原始的上边距.  方便刷新之后,把 scrollView 的contentInset改回这个位置.
    _edgeInsetsTop = self.scrollView.contentInset.top;
    
    if (_headerState == HLRefreshHeaderStateRefreshing) {
        return;
    }
    
    //当前的偏移量
    CGFloat contentOffsetY = self.scrollView.contentOffset.y;
    //scrollView左上角 原始偏移量(默认是0),在有导航栏的情况下可能会被调整为64.
    CGFloat happenOffsetY = -self.scrollView.contentInset.top;
    
    //如果往上滑动,直接 return
    if (contentOffsetY >= happenOffsetY) return;
    //header 完全出现时的contentOffset.y
    CGFloat headerCompleteDisplayContentOffsetY = happenOffsetY - kHeaderHeight;
//    NSLog(@"%f  %f  %f",contentOffsetY,happenOffsetY,headerCompleteDisplayContentOffsetY);
    if (self.scrollView.isDragging == YES) {//如果正在拖拽
        //如果当前状态是 HLRefreshStateIdle(闲置状态或者叫正常状态) && header 已经全部显示
        if (_headerState == HLRefreshHeaderStateIdle && contentOffsetY < headerCompleteDisplayContentOffsetY) {
            //将状态设置为  松开就可以进行刷新的状态
            self.headerState = HLRefreshHeaderStatePulling;
//            NSLog(@"下拉状态");
        }else if (_headerState == HLRefreshHeaderStatePulling && contentOffsetY > headerCompleteDisplayContentOffsetY){//如果当前状态是 HLRefreshStatePulling(松开就可以进行刷新的状态) && header只显示了一部分(用户往上滑动了)
            self.headerState = HLRefreshHeaderStateIdle;
//            NSLog(@"常态");
        }
    }else{//如果松开了手
        if (_headerState == HLRefreshHeaderStatePulling) {//如果状态是1,下拉状态.让它进入刷新状态
            self.headerState = HLRefreshHeaderStateRefreshing;
//            NSLog(@"刷新中");
        }
    }
}

即可监测添加下拉刷新控件的UIScrollViewcontentOffset从而判断刷新状态,这里的
HLRefreshHeaderStateIdleHLRefreshHeaderStatePullingHLRefreshHeaderStateRefreshing是枚举值,定义如下:

typedef NS_ENUM(NSUInteger, HLRefreshHeaderState){
    HLRefreshHeaderStateIdle,         //闲置状态
    HLRefreshHeaderStatePulling,      //松开就可以进行刷新的状态
    HLRefreshHeaderStateRefreshing    //正在刷新中的状态
};

2.UIScrollView的三个属性contentOffsetcontentSizecontentInset

@property(nonatomic)  CGPoint   contentOffset;   // default CGPointZero;//contentSize是scrollview可以滚动的区域,比如frame = (0 ,0 ,320 ,480) contentSize = (320 ,960),代表你的scrollview可以上下滚动,滚动区域为frame大小的两倍
@property(nonatomic)  CGSize    contentSize;      // default CGSizeZero;//contentOffset是scrollview当前显示区域顶点相对于frame顶点的偏移量,比如上个例子你拉到最下面,contentoffset就是(0 ,480),也就是y偏移了480;
@property(nonatomic)  UIEdgeInsets   contentInset;     // default UIEdgeInsetsZero. add ;//contentInset是scrollview的contentview的顶点相对于scrollview的位置,例如你的contentInset = (0 ,100),那么你的contentview就是从scrollview的(0 ,100)开始显示;

这里重点要用的就是contentOffsetcontentInset

contentOffset是监测下拉位移以判断刷新状态;
contentInset是用来在显示刷新的头View(例如上面的refresh)时将UIScrollView的contentview下移一个头View的高度以显示头View,刷新结束后恢复原先的contentInset

在整个过程中,头View(refresh)一直是贴在UIScrollView的contentview顶部,只是contentInset为初始状态是在屏幕外,当contentview下移一个头View的高度后则显示出来。


理解上面两点后,基本就可以 去实现自己的下拉刷新控件了,而上拉加载更多的实现原理也基本一样。


相关文章

  • iOS 下拉刷新实现原理

    最近研究iOS的下拉刷新,想自己封装个下拉刷新控件,所以首先研究了下实现的原理。 这里主要涉及到两个知识点,如下:...

  • UITableView 下拉刷新的实现

    UITableView下拉刷新的实现原理是自定义的下拉刷新控件KVO监听UITableView(UIScrollV...

  • iOS开发 下拉刷新上拉加载更多详解

    下拉刷新(上拉加载更多)是大家经常用到的功能,本篇文章将带大家详细介绍下拉刷新原理,一步步实现下拉刷新效果。下拉刷...

  • 下拉刷新实现原理

    参考资料1参考资料2实现原理:使用组合View的方式,先定义一个LinearLayout,LinearLayout...

  • MJRefresh原理分析

    国内好多开发者选择MJRefresh来实现下拉刷新,最近我也在读他的源码,在这我分享下我理解的实现的原理 下拉刷新...

  • 简单代码实现H5下拉刷新和触底加载更多

    首先说一下实现原理:下拉刷新实现下拉刷新主要分为三步:监听原生touchstart事件,记录其初始位置的值,e.t...

  • 自定义下拉刷新

    实现的效果如下: 下拉刷新原理 其实原理很简单: 0.UIScrollView的contentInset属性:用于...

  • ListView下拉刷新实现方式详解和改造(下)

    上一节,我给大家讲了ListView通过setPaddingTop去实现下拉刷新的原理。链接:ListView下拉...

  • MJRefresh原理分析

    MJRefresh是流行的下拉刷新控件,前段时间为了修复一个BUG,读了它的源码,本文总结一下实现的原理 下拉刷新...

  • IOS上拉 下拉刷新

    iOS下拉刷新和上拉刷新 在iOS开发中,我们经常要用到下拉刷新和上拉刷新来加载新的数据,当前这也适合分页。iOS...

网友评论

    本文标题:iOS 下拉刷新实现原理

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