博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
UITableView:下拉刷新和上拉加载更多
阅读量:7050 次
发布时间:2019-06-28

本文共 5602 字,大约阅读时间需要 18 分钟。

【转载请注明出处】

 

本文将说明让UIScrollView支持"下拉刷新"和"上拉加载更多"的实现机制,并实现一个可用的tableView子类,以下主要以"下拉刷新"进行说明。

工程地址在帖子最下方,只需要代码的直拉到底即可。

 

目录

1、contentInset和下拉刷新;

2、动画、动态文字和刷新时间;

3、其他;

4、工程地址。 

 

【added at 2013.11.28】

下拉刷新和section headerView冲突原因分析及解决办法。 

 

1、contentInset和下拉刷新

contentInset是UIScrollView的属性,它描述了UIScrollView的内容View的内边距,具体可见官方文档:

目前几乎所有"下拉刷新"的第三方库都是依赖它实现的。

【为便于讨论,将下拉刷新/上拉加载时显示的视图称为refresh panel,如下图】

 

 

在用户手指向下滑动到最终更新界面的过程中,经历了4个步骤:

(1)随着用户下拉逐渐显示UITableView顶部的refresh panel;

(2a)下拉达到预设位置,状态文字变为"松开可以刷新";

(2b)下拉未达到预设位置,用户手指离开屏幕,ScrollView弹回,refresh panel重新隐藏起来,结束。

(3)用户手指离开屏幕,refresh panel保持显示。状态文字变为"加载中",在后台执行更新数据的操作;

(4)数据更新完成,返回主线程,重新隐藏refresh panel,结束。

 

可以看到,如果不考虑刷新时间、状态文字等,实现"下拉刷新"实际上只需要做到2件事:

(1)隐藏refresh panel(初始时和刷新后)

隐藏refresh panel,即使其居于UITableView的上方且不可见,如下

1 - (void)addDragHeaderView 2 { 3     if (self.shouldShowDragHeader && !dragHeaderView) 4     { 5         CGRect frame = CGRectMake(0, -self.dragHeaderHeight, 6                                     self.bounds.size.width, self.dragHeaderHeight); 7         dragHeaderView = [[Pull2RefreshView alloc] 8                                     initWithFrame:frame type:kPull2RefreshViewTypeHeader]; 9         [self addSubview:dragHeaderView];10     }11 }

注意:不应使用UITableView的tableHeaderView来作为refresh panel,一来会使得下拉刷新和自定义tableHeaderView无法共存,二来UITableView的内容视图是包含tableHeaderView的,即

tableView.contentSize.height == tableView.tableHeaderView.height

                                          + n * sectionHeaderView.height

                                          + m * cell.height

                                          + tableView.tableFooterView.height

因此想让tableHeaderView默认不可见,需要修改contentOffset的初始值并在用户滑动时控制滑动范围,比较麻烦。】

(2)显示refresh panel

当用户手指下拉达到预设值并离开屏幕,立即修改contentInset,使refresh panel保持显示,如下

1 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate2 {3         if (scrollView.contentOffset.y < -self.dragHeaderHeight - 10.0f4         {5             //使refresh panel保持显示6             self.contentInset = UIEdgeInsetsMake(self.dragHeaderHeight, 0, 0, 0);7         }8 }

如此,更新数据后再次隐藏refresh panel的方式也很明了

1 self.contentInset = UIEdgeInsetsZero;

 

2、动画、动态文字和刷新时间

一个标准的refresh panel(如新浪微博的),包含指示箭头、加载菊花、状态文字和更新时间四部分,如下

1 @implementation Pull2RefreshView 2 { 3     UILabel     *hintLabel; 4     UILabel     *timeLabel; 5      6     UIImageView             *arrowImageView; 7     UIActivityIndicatorView *indicatorView; 8      9     Pull2RefreshViewType refreshType;10 }

在1中已经做到了refresh panel的显示的隐藏,2中只需要在合适的时候改变refresh panel的显示内容即可。

(1)"下拉可以刷新"—>"松开立即更新"

在UIScrollView的委托函数scrollViewDidScroll:中检测用户下拉的程度,达到预设值后就改变状态,如下:

1 - (void)scrollViewDidScroll:(UIScrollView *)scrollView 2 { 3     //拉动足够距离,状态变更为“松开....” 4     if (self.shouldShowDragHeader && dragHeaderView) 5     { 6         if (dragHeaderView.state == kPull2RefreshViewStateDragToRefresh 7             && scrollView.contentOffset.y < -self.dragHeaderHeight - 10.f 8             && !headerRefreshing 9             && !footerRefreshing)10         {11             [dragHeaderView flipImageAnimated:YES];12             [dragHeaderView setState:kPull2RefreshViewStateLooseToRefresh];13         }14     }15 }

修改指示箭头方向为向上,在setState中修改状态文本为"松开立即刷新"。

(2)"松开立即刷新"—>"加载中..."

在UIScrollView的委托函数scrollViewDidEndDragging: willDecelerate:中检测用户手指离开屏幕时的情况:

1 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 2 { 3         //拉动足够距离,松开后,状态变更为“加载中...” 4         if (dragHeaderView.state == kPull2RefreshViewStateLooseToRefresh 5             && scrollView.contentOffset.y < -self.dragHeaderHeight - 10.0f) 6         { 7             //使refresh panel保持显示 8             self.contentInset = UIEdgeInsetsMake(self.dragHeaderHeight, 0, 0, 0); 9             [dragHeaderView flipImageAnimated:YES];10             [dragHeaderView setState:kPull2RefreshViewStateRefreshing];11         }12 }

相比1中添加了修改指示箭头方向,在setState中修改状态文本为"加载中..."。

(3)"加载中..."—>"下拉可以刷新"

这一步需要由外部(通常是ViewController)判断何时执行,提供一个方法供外部调用,如下:

1 - (void)completeDragRefresh 2 { 3         [UIView beginAnimations:nil context:NULL]; 4         [UIView setAnimationDuration:0.3f]; 5         self.contentInset = UIEdgeInsetsZero; 6         [UIView commitAnimations]; 7          8         [dragView flipImageAnimated:NO]; 9         [dragView setState:kPull2RefreshViewStateDragToRefresh];10 }

指示箭头方向和状态文本恢复为初始状态,更新时间变为当前时间。

 

3、其他

(1)"下拉刷新"和"上拉加载更多"的不同
"下拉刷新"的refresh panel的位置始终不变,而"上拉加载更多"的refresh panel则需要随着tableView.contentSize的变化而变化。一个比较简单的方案是:

1 tableView.tableFooterView = dragFooterView;

在某些第三方实现中便是如此处理的,好处是简单到只需要一行代码,坏处是tableFooterView被占用了。考虑到tableFooterView在"上拉加载更多"的情境下不太需要自定义,影响不大。

另一个方案是在初始化时和数据更新后,设置refresh panel的frame使其始终保持正确位置。

demo中用了第1种方法,SVPullToRefresh则采用了第2种方法。

此外,在触发刷新的条件上,二者也是不同的。"下拉刷新"时,为防止刷新"太过灵敏",需要设置一个阀值来控制,所以才有"松开立即刷新"。而"上拉加载更多"是在用户往下不断浏览内容的过程中触发的,因此只需滑动到内容底部就立即触发加载。

 

(2)如何封装

UITableView作为派生类,是和基类UIScrollView共享一个delegate属性的,即UITableViewDeleagte和UIScrollViewDelegate是同时指定的。这带来的问题是,想要封装一个支持"下拉刷新"和"上拉加载更多"的UITableView子类,恐怕不得不增加一层委托,将UITableViewDelegate中的各种方法都转到外部进行实现,如

1 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath2 {3     if (self.pullDelegate && [self.pullDelegate respondsToSelector:@selector(pull2RefreshTableView:didSelectRowAtIndexPath:)])4     {5         [self.pullDelegate pull2RefreshTableView:self didSelectRowAtIndexPath:indexPath];6     }7 }

可谓麻烦至极。

Added 2014.7.24:SVPullToRefresh中,提供了一种很好的思路,通过KVO监视UIScrollView的contentOffset.y的变化,来判断是否会触发下拉刷新或上拉加载,有兴趣的可以直接看它的源码:

  

(3)下拉刷新和UITableView的section headerView冲突

 由于内容较多,单独开一帖进行说明,地址:

4中工程已进行相应修改。 

 

 

4、封装的Pull2RefreshTableView Demo工程

使用iOS 6.1 SDK编译,使用ARC。 

地址:

 

【参考】

1、Scroll View Programming Guide for iOS

2、SVPullToRefresh

 

转载于:https://www.cnblogs.com/lexingyu/p/3439707.html

你可能感兴趣的文章
Docker软件安装系列。
查看>>
我的友情链接
查看>>
LeetCode-330.Patching Array
查看>>
Linux下用Java获取本机IP
查看>>
Eclipse的Spring库导入
查看>>
velocity 判断 变量 是否不是空或empty
查看>>
获得数据库自动生成的主键
查看>>
Hibernate缓存机制
查看>>
【BZOJ】1415 [Noi2005]聪聪和可可 期望DP+记忆化搜索
查看>>
android 7.1 调用相机崩溃解决办法
查看>>
------第二节-----------------第二讲----单链表的基本操作---------
查看>>
delegate代理设计模式
查看>>
花10分钟搞懂开源框架吧 - 【NancyFx.Net】
查看>>
GridView(网格视图)+MotionEvent(触控事件)实现可以拖动排序的网格图
查看>>
显示/隐藏Mac下的隐藏文件
查看>>
POJ-3565 Ants 空间点对不相交匹配-最小权值匹配
查看>>
springmvc(一)
查看>>
Hibernate与 MyBatis的比较
查看>>
awk调用shell命令的两种方法:system与print
查看>>
谷歌开源第二代机器学习系统 TensorFlow
查看>>