ios 上下拉刷新
UITableView:下拉刷新和上拉加载更多 - cDigger
【转载请注明出处】
本文将说明让UIScrollView支持"下拉刷新"和"上拉加载更多"的实现机制,并实现一个可用的tableView子类,以下主要以"下拉刷新"进行说明。
工程地址在帖子最下方,只需要代码的直拉到底即可。
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的上方且不可见,如下
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)decelerate
2 {
3 if (scrollView.contentOffset.y < -self.dragHeaderHeight - 10.0f
4 {
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使其始终保持正确位置。
此外,在触发刷新的条件上,二者也是不同的。"下拉刷新"时,为防止刷新"太过灵敏",需要设置一个阀值来控制,所以才有"松开立即刷新"。而"上拉加载更多"是在用户往下不断浏览内容的过程中触发的,因此只需滑动到内容底部就立即触发加载。
UITableView作为派生类,是和基类UIScrollView共享一个delegate属性的,即UITableViewDeleagte和UIScrollViewDelegate是同时指定的。这带来的问题是,想要封装一个支持"下拉刷新"和"上拉加载更多"的UITableView子类,恐怕不得不增加一层委托,将UITableViewDelegate中的各种方法都转到外部进行实现,如
1 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
2 {
3 if (self.pullDelegate && [self.pullDelegate respondsToSelector:@selector(pull2RefreshTableView:didSelectRowAtIndexPath:)])
4 {
5 [self.pullDelegate pull2RefreshTableView:self didSelectRowAtIndexPath:indexPath];
6 }
7 }
可谓麻烦至极。暂未想到较好的解决方案。【Mark】
4、封装的Pull2RefreshTableView Demo工程
使用iOS 6.1 SDK编译,使用ARC。
地址: https://github.com/cDigger/CDPullToRefreshDemo
1、Scroll View Programming Guide for iOS
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{ // scrollView == self.tableView == self.view // 如果tableView还没有数据,就直接返回 if (self.statuses.count == 0 || self.tableView.tableFooterView.isHidden == NO) return; CGFloat offsetY = scrollView.contentOffset.y; // 当最后一个cell完全显示在眼前时,contentOffset的y值 CGFloat judgeOffsetY = scrollView.contentSize.height + scrollView.contentInset.bottom - scrollView.height - self.tableView.tableFooterView.height; if (offsetY >= judgeOffsetY) { // 最后一个cell完全进入视野范围内 // 显示footer self.tableView.tableFooterView.hidden = NO; // 加载更多的微博数据 [self loadMoreStatus]; }} |
ios 上下拉刷新的更多相关文章
- iOS下拉刷新和上拉刷新
在iOS开发中,我们经常要用到下拉刷新和上拉刷新来加载新的数据,当前这也适合分页.iOS原生就带有该方法,下面就iOS自带的下拉刷新方法来简单操作. 上拉刷新 1.在TableView里,一打开软件, ...
- 高仿IOS下拉刷新的粘虫效果
最近看需要做一款下拉刷新的效果,由于需要和Ios界面保持一致,所以这用安卓的方式实现了ios下的下拉刷新的粘虫效果. 最新的安卓手机版本的QQ也有这种类似的效果,就是拖动未读信息的那个红色圆圈,拖动近 ...
- iOS 下拉刷新-上拉加载原理
前言 讲下拉刷新及上拉加载之前先给大家解释UIScrollView的几个属性 contentSize是UIScrollView可以滚动的区域. contentOfinset 苹果官方文档的解释是&qu ...
- iOS 下拉刷新和加载更多 (OC\Swift)
Swift语言出来之后, 可能还没有第三方的下拉刷新和上提加载, 所以自己用UIRefreshControl控件和UITableView实例的tableFooterView(底部视图)属性结合起来写了 ...
- iOS 下拉刷新 上拉加载实现原理
1.下拉刷新 实现原理 if (scrollView.contentOffset.y < -100) { [UIView animateWithDuration:1.0 animations:^ ...
- iOS:下拉刷新控件UIRefreshControl的详解
下拉刷新控件:UIRefreshControl 1.具体类信息: @interface UIRefreshControl : UIControl //继承控制类 - (instancetype)ini ...
- ios 下拉刷新开源框架 MJRefresh
gitHub 下载框架 搜索MJExampleViewController.h 下拉刷新 MJTableViewController 上拉刷新 MJTableViewController Collec ...
- 仿IOS中下拉刷新的“雨滴”效果
在IOS中,有非常赞的"水滴"下拉效果.非常久之前也想在Android上实现,可是苦于能力有限,一直未能付诸行动.这几天趁着空隙时间.写了一版初步实现,基本达到了"水滴& ...
- iOS 上拉刷新和下拉加在更多(第三方框架EGOTableViewPullRefresh)
#import <UIKit/UIKit.h> @interface AppDelegate : UIResponder <UIApplicationDelegate> @pr ...
随机推荐
- ios 第三方登录
ios 第三方登录通常的做法是, 1.登录后,获得第三方的uid,调用服务器登录接口,如服务器返回无此用户,转2,有此用户,转3. 2.服务器返回无此用户,需完善用户资料,如果完成,则在服务器创建与第 ...
- Javascript高级程序设计——面向对象之实现继承
原型链: 构造函数中都有一个prototype属性指针,这个指针指向原型对象,而创建的实例也有指向这个原型对象的指针__proto__.当实例查找方法时先在实例上找,找不到再通过__proto__到原 ...
- SQL server 语句新建用户、对用户授权、删除用户实例
Grant select on tb to db_user --给db_user用户授权 tb表 查询权限 一.命令操作 USE mydb GO --1. 新建测试用户 --1.1 添加登录用户和密码 ...
- 解决IE6不支持position:fixed的bug
/*完整代码 */ /* 除IE6浏览器的通用方法 */ .ie6fixedTL { position: fixed; left:; top:; } .ie6fixedBR { position: f ...
- iOS开发——UI进阶篇(十四)modal
一.modal与pushmodal从下面往上盖住原来的控制器,一般上一个控制器和下一个控制器没有什么关联时用modal,比如联系人的加号跳转页面,任何控制器都可以用modal push一般是上下文有关 ...
- [转载]JavaEE学习篇之——Session&&Cookie
原文链接: http://blog.csdn.net/jiangwei0910410003/article/details/23337043 今天继续来看看JavaWeb的相关知识,这篇文章主要来讲一 ...
- PHP面向对象常见的关键字和魔术方法
在PHP5的面向对象程序设计中提供了一些常见的关键字,用来修饰类.成员属性或成员方法,使他们具有特定的功能,例如final.static.const等关键字.还有一些比较实用的魔术方法,用来提高类或对 ...
- 绝对实用 NAT + VLAN +ACL管理企业网络
在企业中,要实现所有的员工都能与互联网进行通信,每个人各使用一个公网地址是很不现实的.一般,企业有1个或几个公网地址,而企业有几十.几百个员工.要想让所有的员工使用这仅有的几个公网地址与互联网通信该怎 ...
- maven简单配置
maven-3.3.9下载 Maven是一个项目管理和综合工具.Maven提供了开发人员构建一个完整的生命周期框架.开发团队可以自动完成项目的基础工具建设,Maven使用标准的目录结构和默认构建生命周 ...
- ext grid 子表格
Ext.define('app.view.main.biz.customer.receipt.followup.FollowUpActionPanel', { extend: 'Ext.grid.Pa ...