美文网首页IOS开发笔记iOS Developer编程知识点
IOS UI篇-仿微博下拉刷新和上拉加载

IOS UI篇-仿微博下拉刷新和上拉加载

作者: Bob_Blog | 来源:发表于2017-03-08 10:53 被阅读710次

    前言

    • 关于下拉刷新和上拉加载,以前开发都是直接使用第三方MJRefresh,1-2句代码集成,使用方便,省事;最近项目上架后比较闲,就研究了一下MJ,搜集了一些资料,自己模仿微博,写了一个刷新控件。

    效果图

    效果图.gif

    应用知识点

    • kvo

    加载进父视图时,注册观察者,实时监听UIScrollView的contentOffset的变化,根据变化展示不一样的刷新状态,执行相应的操作。

    pragma mark -加入父视图时添加观察者
    - (void)willMoveToSuperview:(UIView *)newSuperview {
        [super willMoveToSuperview:newSuperview];
        if (newSuperview) {
            self.superScrollview = (UIScrollView *)newSuperview;
            [self.superScrollview addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
        }else {
            if (self.superScrollview) {
                [self.superScrollview removeObserver:self forKeyPath:@"contentOffset"];
                
            }
        }
    }
    
    #pragma mark-KVO的代理方法
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
        if ([keyPath isEqualToString:@"contentOffset"]) {
      
            if (self.superScrollview.contentInset.top==64) {
                
                self.contentOffSetY=self.superScrollview.contentInset.top;
            }
            CGFloat y = self.superScrollview.contentOffset.y;
            if (self.superScrollview.isDragging) {
                //正在拖动
                
                if (y<  -self.contentOffSetY &&y> -self.contentOffSetY - headerRefeshHight && self.currentState ==TBStatuePulling) {
                    //下拉状态->正常状态
                    self.currentState = TBStatueNomal;
                    
                }else if (y <= -self.contentOffSetY - headerRefeshHight && self.currentState == TBStatueNomal)
                    //正常状态->下拉状态
                {
                    self.currentState = TBStatuePulling;
                }
            }else if(self.currentState ==TBStatuePulling &&y <= -self.contentOffSetY - headerRefeshHight){ //拖拽释放
                self.currentState = TBStatueRefreshing;
            }
    
        }
    }
    
    • RunTime
      Runtime 很强大,需要研究的地方也很多,这里就不多讲了,这里主要用到的关联对象。
      首先来说,关联对象,我们需要将tableView添加下拉刷新和下拉加载,系统并没有这个属性,我们需要写个UISCrollView的类别,但我们知道Category只能添加方法,不能添加实例变量,如果只是添加属性,其实是添加的setter和getter方法,不会自动生成实例变量的。
      这时候Runtime就发挥它的作用了
    .h文件
    @class TBRefreshHeadView;
    
    @class TBRefreshFootView;
    
    @interface UIScrollView (TBRefresh)
    //下拉刷新
    @property(nonatomic,weak)TBRefreshHeadView *header;
    //上拉加载
    @property(nonatomic,weak)TBRefreshFootView *footer;
    
    //添加下拉刷新方法
    -(void)addRefreshHeaderWithBlock:(void (^)())Block;
    
    //添加上拉刷新方法
    -(void)addRefreshFootWithBlock:(void (^)())Block;
    
    .m文件
    #pragma mark-关联头部
    -(void)setHeader:(TBRefreshHeadView *)header
    {
        
       objc_setAssociatedObject(self, @selector(header), header, OBJC_ASSOCIATION_ASSIGN);
        
    }
    
    -(TBRefreshHeadView*)header
    {
         return objc_getAssociatedObject(self, @selector(header));
    }
    
    
    #pragma mark-关联底部
    -(void)setFooter:(TBRefreshFootView *)footer
    {
        
        objc_setAssociatedObject(self, @selector(footer), footer, OBJC_ASSOCIATION_ASSIGN);
        
    }
    
    -(TBRefreshFootView*)footer
    {
        
        return objc_getAssociatedObject(self, @selector(footer));
        
    }
    
    #pragma mark-初始化头部
    -(void)addRefreshHeaderWithBlock:(void (^)())Block
    {
        TBRefreshHeadView *TBheader=[TBRefreshHeadView new];
        
        TBheader.ReturnBlock=Block;
        
        self.header=TBheader;
        
        [self insertSubview:TBheader atIndex:0];
        
    }
    
    #pragma mark-初始化底部
    -(void)addRefreshFootWithBlock:(void (^)())Block
    {
        
        TBRefreshFootView *TBfooter=[TBRefreshFootView new];
        
        TBfooter.ReturnBlock=Block;
        
        self.footer=TBfooter;
        
        [self insertSubview:TBfooter atIndex:0];
    
    }
    

    调用

    刷新头

    //开始下拉刷新
    -(void)beginRefreshing;
    
    //结束下拉刷新
    -(void)endHeadRefresh;
    

    加载底部

    //结束下拉加载
    - (void)endFooterRefreshing;
    
    //没有更多数据
    -(void)NoMoreData;
    
    //将没有更多数据状态设置为正常状态
    -(void)ResetNomoreData;
    

    使用

    -(UITableView*)mainTableview
    {
        if (!_mainTableview) {
        
            __weak ViewController *weakself=self;
            _mainTableview=[[UITableView alloc]initWithFrame:CGRectMake(0,0, self.view.frame.size.width, self.view.frame.size.height) style:UITableViewStylePlain];
            
            _mainTableview.delegate=self;
            
            _mainTableview.dataSource=self;
            
            [_mainTableview addRefreshHeaderWithBlock:^{
              
                [weakself LoadDatas];
                
            }];
            
            [_mainTableview addRefreshFootWithBlock:^{
              
                [weakself LoadMoreDatas];
            }];
       
        }
        return _mainTableview;
    }
    

    注意:移除观察者

    移除观察者的时候,发现tableview是先于刷新头和刷新尾释放的,所以在tableView调用dealloc方法时,先将移除观察者,然后将刷新头和刷新尾置nil,发现dealloc方法会调用2次,查了资料发现。一次是 scrollView(实际tableView),另一次是UITableViewWrapperView,这个类是我们直接使用的。这种情况会出现在tableView上。scrollView 和 collectionView 不会出现,所以我做了以下处理:

    #pragma mark-测试可得,tableView先释放,所以在释放之前把头部和尾部释放,移除观察者,不然会奔溃
    -(void)dealloc
    {
        if (self.header) {
            [self removeObserver:self.header forKeyPath:@"contentOffset"];
            self.header=nil;
        }
        
        if (self.footer) {
            [self removeObserver:self.footer forKeyPath:@"contentOffset"];
            
            self.footer=nil;
        }
    }
    
    

    最后

    这个我第一篇简书,写的不好,大家多多指教!
    附上github地址demo地址,ios 路还长,学习的很多,以后会继续下去!
    参考资料:

    相关文章

      网友评论

        本文标题:IOS UI篇-仿微博下拉刷新和上拉加载

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