refreshControl 是UITableViewController 里面一个属性,但是默认情况下是nil ,所以在使用其的时候需要创建,及其指定标题。一般情况下,我们有两种需求,一是下拉刷新,二是上拉滚动刷新新增多条数据。这两个需求用得比较频繁,下面来看看了两种需求的是怎样的运作情况。
第一种方式是:下拉刷新的方式相对容易处理,监听UIRefreshControl 的UIControlEventValueChanged 事件后 当下拉的时候,该滚动的效果就会出来了。
@interface MainViewController ()
@property (nonatomic,strong) NSMutableArray *queryArray;
@property (nonatomic) NSInteger count;
@end
- (void)viewDidLoad
{
[super viewDidLoad];
self.count = 0;
self.refreshControl = [[UIRefreshControl alloc]init];
self.refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:@"刷新中..."];
[self.refreshControl addTarget:self action:@selector(onRereshTableView) forControlEvents:UIControlEventValueChanged];
}
-(void) onRereshTableView
{
//延迟执行
[self performSelector:@selector(handlerData) withObject:nil afterDelay:2];
}
-(void) handlerData
{
[self.refreshControl endRefreshing];
self.count = 0;//默认不分页
[self getNewData];
}
-(void) getNewData
{
[_queryArray removeAllObjects];//清理数据
}
第二种方式是 :(引用一个博客的文章来源做法),该方法主要在滚动条滚动完成后那里下手处理这个问题。也就是说当我们滚动到一定高度的时候,超出了原来的scrollView.contentSize.height的时候,原则理论上我们就执行刷新获取更多的内容方法。这里有一个细节的处理,就是递增数使用(统计你上拉刷新的次数)。我们知道,获取从服务器得到的数据是大部分来自数据库存款的。所以要进行分页查询的方式获取数据的方式处理这个问题。
分页最初的默认查询数据假设为10条,于是它下一次查询的开始就是从10开始查询 不包括10 。
伪代码如下: sql.limit = 10; sql.skip = self .count *sql.limit;
-(void) scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView.contentOffset.y+scrollView.frame.size.height > scrollView.contentSize.height && scrollView.contentOffset.y >0)
{
//本身的内容高度
CGSize size = self.tableView.contentSize;
size.height+=115;
self.tableView.contentSize = size;
[ self performSelector:@selector(getMoreData) withObject:nil afterDelay:0];
}
}
-(void)getMoreData
{
//不需要清理旧数据,让数组继续添加数据
self.count++;
//下面为处理分页查询的结果
sql.limit = 10;//限定分页查询数
sql.skip = self .count *sql.limit;//跳转分页
....
....
[queryArray addObject:obj];
}
当上拉数据已经刷新完了,数据库已经没发查询到数据内容了,那么scrollViewDidScroll 和 getMoreData 或多或少有点漏洞和问题。所以这里还要完善一下。
我们使用这个组件主要的用于【下拉数据处理的逻辑】。下拉需要在UITableView的数据源清除干净再从远程获取最新时间表的数据。上拉需要在原来的数据源里面继续添加数据方式,并刷新UItableView 的内容高度来进行处理逻辑。一般情况下,我们需要增加一些额外的组件显示在下拉更新的过程。如UIButton 或者 UIActivityIndicatorView 的组件反馈在上拉的时候做出的UI反馈。
-(void) getNewData
{
[_queryArray removeAllObjects];//需要清理旧数据再从后端数据库获取最新数据
//请求网络情况
[_queryArray addObject:obj];
[self.tableView reloadData];
}
-(void)getMoreData
{
//不需要删除,继续添加数据
[_queryArray addObject:obj];
[self.tableView reloadData];
}
如何理解下面的判断? 为什么 scrollView.contentOffset.y 在上拉的过程y是要求大于0 ? 这里我依旧感觉未解的地方。
if (scrollView.contentOffset.y+scrollView.frame.size.height > scrollView.contentSize.height && scrollView.contentOffset.y >0)
scrollView.contentOffset.y 这个正好是往上拉刷新数据 所以这个值必须大于0.
(scrollView.contentOffset.y+scrollView.frame.size.height) 这个和 可以理解为
所以当继续往上拉的时候小框就是会大于scrollView.contentSize.height 。这样子就可以判断获取更多数据了。
你往上拉的时候, contentOffset.y 大于0
你可以理解为大底的框是始终不动的,而是小框在在上下移动,框下移动,视角正好往上移动(contentOffset.y >0),框上移动的时候,视角刚好是往下移动。(contentOffset.y < 0)
可以想象一下。
在开始测试的过程当中 配合 scrollViewDidScroll 和 self.count++; 这种方式存在一个严重弊端,就是无限制去拉伸的时候会不断执行- getMoreData 方法 自增器self.count 就很傻乎乎不断递增递增,这样子在分页查询的时候并不准确。接下来改成下面的方式。
NSInteger page = 0;
page = self.count +1; 改成这种方式
sql.limit = 10; //默认请求分页数
sql.skip = page *sql.limit; //跳转页数获取数据
//请求后端获取数据成功后
才设置正确的当前页数
self.count = page;
这样一来我们就可以解决到 self.count++ 的问题了。
接下来,我们还要继续处理scrollViewDidScroll 的逻辑问题
-(void) scrollViewDidScroll:(UIScrollView *)scrollView
{
//处理滚动条高度的逻辑问题
//不处理tableView的contentSize 的值改而直接
if (scrollView.contentOffset.y+scrollView.frame.size.height > scrollView.contentSize.height && scrollView.contentOffset.y >0)
{
if(self.isRefresing) return;//刷新的时候屏蔽
//处理下拉的动画显示
...
....
[ self performSelector:@selector(getMoreData) withObject:nil afterDelay:0];
}
}
-(void)getMoreData
{
//不需要删除,继续添加数据
if(self.isRefresing) return ;
//设置分页逻辑
...code
//
self.isRefresing = YES;
//成功后新增数据,数据刷新,将刷新状态变换NO,处理掉上拉动画
[_queryArray addObject:obj];
[self.tableView reloadData];
self.isRefresing = NO;
}
经过改动后,效果比第一种处理的方式会好一点。同样也要注意一点是下拉数据删除的时间,是在成功获取后数据才删除,而不是在第一个
-(void) getNewData
{
[_queryArray removeAllObjects];//需要清理数据
//从后端数据库获取最新数据
[_queryArray addObject:obj];
[self.tableView reloadData];
}
而是
-(void) getNewData
{
//调用后端的请求
//从后端数据库获取最新数据回调后有数据才进行删除
[_queryArray removeAllObjects];//需要清理数据
for(NSObject *obj in array)
{
[_queryArray addObject:obj];
}
[self.tableView reloadData];
}
总体而言,第一次接触下拉和上拉刷新多多少少还是有很多问题。而在一些开源类当中,还有其他做法,比如采用KVO 监听滚动条contentoffset的变化,采用这种机制去设计刷新思路还是挺ok 的。
当我们刷新的过多数据的时候,数组保存的东西越多,这个时候内存会紧张。在网易新闻客户端iphone 版本看到一个很小的细节。当分页数出现过多的时候,网易客户端不会主动给客户更新数据采取的方式 是让用户去选择处理按钮点击获取更多数据。