UITableView:下拉刷新和上拉加载更多
【转载请注明出处】
本文将说明让UIScrollView支持"下拉刷新"和"上拉加载更多"的实现机制,并实现一个可用的tableView子类,以下主要以"下拉刷新"进行说明。
工程地址在帖子最下方,只需要代码的直拉到底即可。
【目录】
1、contentInset和下拉刷新;
2、动画、动态文字和刷新时间;
3、其他;
4、工程地址。
【added at 2013.11.28】
下拉刷新和section headerView冲突原因分析及解决办法。
1、contentInset和下拉刷新
contentInset是UIScrollView的属性,它描述了UIScrollView的内容View的内边距,具体可见官方文档:
Scroll View programming Guide for iOS
目前几乎所有"下拉刷新"的第三方库都是依赖它实现的。
【为便于讨论,将下拉刷新/上拉加载时显示的视图称为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的上方且不可见,如下
- (void)addDragHeaderView
{
if (self.shouldShowDragHeader && !dragHeaderView)
{
CGRect frame = CGRectMake(, -self.dragHeaderHeight,
self.bounds.size.width, self.dragHeaderHeight);
dragHeaderView = [[Pull2RefreshView alloc]
initWithFrame:frame type:kPull2RefreshViewTypeHeader];
[self addSubview:dragHeaderView];
}
}
【注意:不应使用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保持显示,如下
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (scrollView.contentOffset.y < -self.dragHeaderHeight - 10.0f
{
//使refresh panel保持显示
self.contentInset = UIEdgeInsetsMake(self.dragHeaderHeight, , , );
}
}
如此,更新数据后再次隐藏refresh panel的方式也很明了
self.contentInset = UIEdgeInsetsZero;
2、动画、动态文字和刷新时间
一个标准的refresh panel(如新浪微博的),包含指示箭头、加载菊花、状态文字和更新时间四部分,如下
@implementation Pull2RefreshView
{
UILabel *hintLabel;
UILabel *timeLabel; UIImageView *arrowImageView;
UIActivityIndicatorView *indicatorView; Pull2RefreshViewType refreshType;
}
在1中已经做到了refresh panel的显示的隐藏,2中只需要在合适的时候改变refresh panel的显示内容即可。
(1)"下拉可以刷新"—>"松开立即更新"
在UIScrollView的委托函数scrollViewDidScroll:中检测用户下拉的程度,达到预设值后就改变状态,如下:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
//拉动足够距离,状态变更为“松开....”
if (self.shouldShowDragHeader && dragHeaderView)
{
if (dragHeaderView.state == kPull2RefreshViewStateDragToRefresh
&& scrollView.contentOffset.y < -self.dragHeaderHeight - .f
&& !headerRefreshing
&& !footerRefreshing)
{
[dragHeaderView flipImageAnimated:YES];
[dragHeaderView setState:kPull2RefreshViewStateLooseToRefresh];
}
}
}
修改指示箭头方向为向上,在setState中修改状态文本为"松开立即刷新"。
(2)"松开立即刷新"—>"加载中..."
在UIScrollView的委托函数scrollViewDidEndDragging: willDecelerate:中检测用户手指离开屏幕时的情况:
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
//拉动足够距离,松开后,状态变更为“加载中...”
if (dragHeaderView.state == kPull2RefreshViewStateLooseToRefresh
&& scrollView.contentOffset.y < -self.dragHeaderHeight - 10.0f)
{
//使refresh panel保持显示
self.contentInset = UIEdgeInsetsMake(self.dragHeaderHeight, , , );
[dragHeaderView flipImageAnimated:YES];
[dragHeaderView setState:kPull2RefreshViewStateRefreshing];
}
}
相比1中添加了修改指示箭头方向,在setState中修改状态文本为"加载中..."。
(3)"加载中..."—>"下拉可以刷新"
这一步需要由外部(通常是ViewController)判断何时执行,提供一个方法供外部调用,如下:
- (void)completeDragRefresh
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3f];
self.contentInset = UIEdgeInsetsZero;
[UIView commitAnimations]; [dragView flipImageAnimated:NO];
[dragView setState:kPull2RefreshViewStateDragToRefresh];
}
指示箭头方向和状态文本恢复为初始状态,更新时间变为当前时间。
3、其他
(1)"下拉刷新"和"上拉加载更多"的不同
"下拉刷新"的refresh panel的位置始终不变,而"上拉加载更多"的refresh panel则需要随着tableView.contentSize的变化而变化。一个比较简单的方案是:
tableView.tableFooterView = dragFooterView;
在某些第三方实现中便是如此处理的,好处是简单到只需要一行代码,坏处是tableFooterView被占用了。考虑到tableFooterView在"上拉加载更多"的情境下不太需要自定义,影响不大。
另一个方案是在初始化时和数据更新后,设置refresh panel的frame使其始终保持正确位置。
demo中用了第1种方法,SVPullToRefresh则采用了第2种方法。
此外,在触发刷新的条件上,二者也是不同的。"下拉刷新"时,为防止刷新"太过灵敏",需要设置一个阀值来控制,所以才有"松开立即刷新"。而"上拉加载更多"是在用户往下不断浏览内容的过程中触发的,因此只需滑动到内容底部就立即触发加载。
(2)如何封装
UITableView作为派生类,是和基类UIScrollView共享一个delegate属性的,即UITableViewDeleagte和UIScrollViewDelegate是同时指定的。这带来的问题是,想要封装一个支持"下拉刷新"和"上拉加载更多"的UITableView子类,恐怕不得不增加一层委托,将UITableViewDelegate中的各种方法都转到外部进行实现,如
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.pullDelegate && [self.pullDelegate respondsToSelector:@selector(pull2RefreshTableView:didSelectRowAtIndexPath:)])
{
[self.pullDelegate pull2RefreshTableView:self didSelectRowAtIndexPath:indexPath];
}
}
可谓麻烦至极。
Added 2014.7.24:SVPullToRefresh中,提供了一种很好的思路,通过KVO监视UIScrollView的contentOffset.y的变化,来判断是否会触发下拉刷新或上拉加载,有兴趣的可以直接看它的源码:
https://github.com/samvermette/SVPullToRefresh
(3)下拉刷新和UITableView的section headerView冲突
由于内容较多,单独开一帖进行说明,地址:
http://www.cnblogs.com/lexingyu/p/3448532.html
4中工程已进行相应修改。
4、封装的Pull2RefreshTableView Demo工程
使用iOS 6.1 SDK编译,使用ARC。
地址:https://github.com/cDigger/CDPullToRefreshDemo
【参考】
1、Scroll View Programming Guide for iOS
2、SVPullToRefresh
https://github.com/samvermette/SVPullToRefresh
UITableView:下拉刷新和上拉加载更多的更多相关文章
- juery下拉刷新,div加载更多元素并添加点击事件(二)
buffer.append("<div class='col-xs-3 "+companyId+"' style='padding-left: 10px; padd ...
- android--------自定义控件ListView实现下拉刷新和上拉加载
开发项目过程中基本都会用到listView的下拉刷新和上滑加载更多,为了方便重写的ListView来实现下拉刷新,同时添加了上拉自动加载更多的功能. Android下拉刷新可以分为两种情况: 1.获取 ...
- Android 自定义 ListView 上下拉动“刷新最新”和“加载更多”歌曲列表
本文内容 环境 测试数据 项目结构 演示 参考资料 本文演示,上拉刷新最新的歌曲列表,和下拉加载更多的歌曲列表.所谓"刷新最新"和"加载更多"是指日期.演示代码 ...
- IOS UITableView下拉刷新和上拉加载功能的实现
在IOS开发中UITableView是非常常用的一个功能,而在使用UITableView的时候我们经常要用到下拉刷新和上拉加载的功能,今天花时间实现了简单的UITableView的下拉刷新和上拉加载功 ...
- 下拉刷新和上拉加载 Swift
转载自:http://iyiming.me/blog/2015/07/05/custom-refresh-and-loading/ 关于下拉刷新和上拉加载,项目中一直使用MJRefresh(原先还用过 ...
- Android 5.X新特性之为RecyclerView添加下拉刷新和上拉加载及SwipeRefreshLayout实现原理
RecyclerView已经写过两篇文章了,分别是Android 5.X新特性之RecyclerView基本解析及无限复用 和 Android 5.X新特性之为RecyclerView添加Header ...
- iscroll.js 下拉刷新和上拉加载
html代码如下 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> < ...
- IOS 开发下拉刷新和上拉加载更多
IOS 开发下拉刷新和上拉加载更多 简介 1.常用的下拉刷新的实现方式 (1)UIRefreshControl (2)EGOTTableViewrefresh (3)AH3DPullRefresh ( ...
- Android 使用PullToRefresh实现下拉刷新和上拉加载(ExpandableListView)
PullToRefresh是一套实现非常好的下拉刷新库,它支持: 1.ListView 2.ExpandableListView 3.GridView 4.WebView 等多种常用的需要刷新的Vie ...
- 使用PullToRefresh实现下拉刷新和上拉加载
使用PullToRefresh实现下拉刷新和上拉加载 分类: Android2013-12-20 15:51 78158人阅读 评论(91) 收藏 举报 Android下拉刷新上拉加载PullToRe ...
随机推荐
- 【poj1182】 食物链
http://poj.org/problem?id=1182 (题目链接) 题意 中文题 Solution 带权并查集. 神犇博客,秒懂 fa记录父亲,r记录与父亲的关系.%3运用的很巧妙. 代码 / ...
- Azure怎么使用ftp登录
1.下载配置文件 2.拷贝FTP的地址 3.查看配置文件里面的用户名和密码 4.登录
- 查看apk包名package和入口activity名称的方法
ctrl+r 打开CMD窗口 进入sdk-aapt目录 执行命令:aapt dump badging xx.apk 内容太多?不好看,没关系,全部拷出来,ctrl+f,so easy! package ...
- xss绕过过滤之方法
很多网站为了避免XSS的攻击,对用户的输入都采取了过滤,最常见的就是对<>转换成<以及>,经过转换以后<>虽然可在正确显示在页面上,但是已经不能构成代码语句了.这个 ...
- nginx负载均衡器处理session共享的几种方法(转)
1) 不使用session,换作cookie 能把session改成cookie,就能避开session的一些弊端,在从前看的一本J2EE的书上,也指明在集群系统中不能用session,否则惹出祸端来 ...
- 通过开源程序同时解决DNS劫持和DNS污染的问题
我们知道,某些网络运营商为了某些目的,对DNS进行了某些操作,导致使用ISP的正常上网设置无法通过域名取得正确的IP地址.常用的手段有:DNS劫持和DNS污染.关于DNS劫持和DNS污染的区别,请查找 ...
- android TP驱动移植调试笔记(转)
1. 添加I2C 设备 TP 一般采用的是I2C 作为数据和命令接口,所以TP 驱动也可以归类为I2C 驱动.TP驱动的主要逻辑不在这里,但是了解了Linux 的I2C 体系架构,就可以对整个驱动流程 ...
- endnote参考文献格式设置
endnote参考文献格式调整: 在office word 2010中加载了endnote x7插件之后,使用过程中发现格式有点乱,不该斜的地方斜体显示,该加方框[]的地方却没有方框,因此从网路上找资 ...
- node.js+websocket实现简易聊天室
(文章是从我的个人主页上粘贴过来的,大家也可以访问我的主页 www.iwangzheng.com) websocket提供了一种全双工客户端服务器的异步通信方法,这种通信方法使用ws或者wss协议,可 ...
- 当前标识(IIS APPPOOL\DefaultWebSite)没有对“C:\Windows\Microsoft.NET\Framework64\v2.0.50727\Temporary ASP.NET Files“的写访问权限
将C#写的webservice发布到IIS后,通过浏览器访问测试,出现如下错误: 根据提示:对Tempory ASP.NET Files没有写访问权限,在资源管理其中定位到这个地址,发现没有这个文件夹 ...