ios开发中,大量的用到了表格table。用到table的时候,有个常用的功能就是数据更新。通常有两种刷新方式:
1. 将表头往下拉
2. 将表尾往上拉
随便google了一下,这种例子很多,但是好像大多数都是表头往下拉的情况。因为工作中需要两种拉动效果,所以就不得不自己高一下了。
从网上下载了一个例子,也不知道从哪里下的,忘了。然后自己动手改了一下,支持两种拉动方式。
刷新控件
网上down的两个文件:EGORefreshTableHeaderView.h和EGORefreshTableHeaderView.m
里面有个class叫做EGORefreshTableHeaderView,给其中一个初始化函数加了个参数,用来指明头还是尾,如:
- (id)initWithFrame:(CGRect)frame IsHeader: (BOOL)bHeader;
至于具体的改动,这里就不介绍了,回头我把代码传上来,大家可以自己看。
如何使用
先创建一个Table,参考http://blog.csdn.net/zj510/article/details/8444310
然后我们可以给这个table加上刷新功能。
增加2个成员变量,一个是头,一个是尾
EGORefreshTableHeaderView *_refreshHeaderView;
EGORefreshTableHeaderView *_refreshFooterView;
增加一个_reloading变量,这个是用来控制并发的,就是不允许同时有多个刷新。
看caller的代码:
//
// KViewController.h
// DragList
//
// Created by Kevin on 12-12-27.
// Copyright (c) 2012年 Kevin. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "EGORefreshTableHeaderView.h"
@interface KViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, EGORefreshTableHeaderDelegate>
{
NSMutableArray* aryItems;
EGORefreshTableHeaderView *_refreshHeaderView;
EGORefreshTableHeaderView *_refreshFooterView;
BOOL _reloading;
int prevItemCount;
}
@property (retain, nonatomic) IBOutlet UITableView *MyTableView;
@end
注意caller类需要实现一个protocol:EGORefreshTableHeaderDelegate。
这个protocol有几个函数需要实现,看它的定义:
@protocol EGORefreshTableHeaderDelegate
- (void)egoRefreshTableHeaderDidTriggerRefresh:(EGORefreshTableHeaderView*)view;
- (BOOL)egoRefreshTableHeaderDataSourceIsLoading:(EGORefreshTableHeaderView*)view;
@optional
- (NSDate*)egoRefreshTableHeaderDataSourceLastUpdated:(EGORefreshTableHeaderView*)view;
@end
函数
egoRefreshTableHeaderDidTriggerRefresh就是刷新响应函数,我们可以在这里更新数据
函数egoRefreshTableHeaderDataSourceIsLoading用来标记当前是否有刷新任务在运行,我们的这个例子不可以同时有多个刷新任务
函数egoRefreshTableHeaderDataSourceLastUpdated是可选的,但是通常我们都会使用,就是返回一个更新完成的时间
ok,现在我们来看看我们的测试代码:
#pragma mark -
#pragma mark EGORefreshTableHeaderDelegate Methods
- (void)egoRefreshTableHeaderDidTriggerRefresh:(EGORefreshTableHeaderView*)view{
// NSLog(@"header: %@, footer: %@, view: %@", _refreshHeaderView, _refreshFooterView, view);
if (view == _refreshFooterView) {
NSLog(@"It's footer");
_reloading = YES;
[NSThread detachNewThreadSelector:@selector(GetMoreFooterData) toTarget:self withObject:nil];
}
if (view == _refreshHeaderView) {
NSLog(@"It's header");
_reloading = YES;
[NSThread detachNewThreadSelector:@selector(GetMoreHeaderData) toTarget:self withObject:nil];
}
}
- (BOOL)egoRefreshTableHeaderDataSourceIsLoading:(EGORefreshTableHeaderView*)view{
return _reloading; // should return if data source model is reloading
}
- (NSDate*)egoRefreshTableHeaderDataSourceLastUpdated:(EGORefreshTableHeaderView*)view{
return [NSDate date]; // should return date data source was last changed
}
这些代码相当的简单,
egoRefreshTableHeaderDidTriggerRefresh里面判断一下是头还是尾,然后调用各自的更新数据线程,注意需要将_reloading设置尾YES,表示已经有刷新任务在运行了。
egoRefreshTableHeaderDataSourceIsLoading简单的返回_reloading
egoRefreshTableHeaderDataSourceLastUpdated返回当前时间。
接下来看看刷新线程
//模拟一些数据,用户将header往下拉
- (void) GetMoreHeaderData
{
@autoreleasepool {
// [NSThread sleepForTimeInterval:5];
for (int i = 3; i > 0; i--) {
NSString* str = [NSString stringWithFormat:@"item header %d", i];
[aryItems insertObject:str atIndex:0];
}
[self performSelectorOnMainThread:@selector(FinishedLoadMoreHeaderData) withObject:nil waitUntilDone:YES];
}
}
//用户将footer往上拉的时候,放一些模拟数据
- (void) GetMoreFooterData
{
@autoreleasepool {
prevItemCount = [aryItems count];
for (int i = 1; i < 10; i++) {
NSString* str = [NSString stringWithFormat:@"item footer %d", i];
[aryItems addObject:str];
}
[self performSelectorOnMainThread:@selector(FinishedLoadMoreFooterData) withObject:nil waitUntilDone:YES];
}
}
我这里创建了2个刷新新城函数,一个模拟3个数据,一个模拟9个数据.注意UI界面的更新不可以在辅助线程里面完成,这个是IOS限制的,那么我们只能在主线程里面更新。然后在辅助线程里面通过performSelectorOnMainThread在主线程里面运行2个函数。增加2个更新ui的函数,如下:
//header下拉处理线程结束会调用这个函数
- (void) FinishedLoadMoreHeaderData
{
//重新装载数据
[_MyTableView reloadData];
[self PutFooterAtEnd];
[_refreshHeaderView refreshLastUpdatedDate];//修改最后更新时间
[_refreshHeaderView egoRefreshScrollViewDataSourceDidFinishedLoading:_MyTableView];
_reloading = NO;
}
//footer上拉处理线程结束会调用这个函数
- (void) FinishedLoadMoreFooterData
{
[_MyTableView reloadData];//重新装载数据
[self PutFooterAtEnd];//将footer放到table的最后
//将滚动条定位到上拉前的那一条item
if (prevItemCount >= 1) {
NSIndexPath* row = [NSIndexPath indexPathForRow:prevItemCount - 1 inSection:0];
[_MyTableView scrollToRowAtIndexPath:row atScrollPosition:UITableViewScrollPositionNone animated:NO];
}
[_refreshFooterView refreshLastUpdatedDate];
_reloading = NO;
[_refreshFooterView egoRefreshScrollViewDataSourceDidFinishedLoading:_MyTableView];
}
代码也是相当的简单,主要需要做这些事情:
1. reload一下table;
2. 调用刷新控件的两个函数:refreshLastUpdateDate和egoRefreshScrollViewDataSourceDidFinishedLoading。
3. 将_reloading设置为NO,意思是一次刷新结束。
需要注意的是表尾刷新结束后,需要调整一下刷新控件的位置,因为表格里面的内容变多了,刷新控件需要往下挪。
看看响应的函数:
//将footer放到table的最后面
- (void) PutFooterAtEnd
{
CGRect footerFrame = _refreshFooterView.frame;
CGRect r = CGRectMake(footerFrame.origin.x, _MyTableView.contentSize.height, _MyTableView.frame.size.width, footerFrame.size.height);
if (r.origin.y < _MyTableView.frame.size.height) {
r.origin.y = _MyTableView.frame.size.height;
}
_refreshFooterView.frame = r;
}
这样就是处理完了 EGORefreshTableHeaderDelegate相关的函数。
现在我们来ViewDidLoad里面初始化table,放一些模拟数据,然后给这个table加上上下刷新功能,如下:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_reloading = NO;
aryItems = [[NSMutableArray alloc] initWithCapacity:0];
[aryItems retain];
for (int i = 0; i < 5; i++) {
NSString* item = [NSString stringWithFormat:@"item %d", i];
[aryItems addObject:item];
}
[self.MyTableView setDataSource:self];
self.MyTableView.delegate = self;
[self ShowHeaderAndFooter];
}
ShowHeaderAndFooter就是用来创建表头刷新和表尾刷新功能的,看:
- (void) ShowHeaderAndFooter
{
if (_refreshHeaderView == nil) {
_refreshHeaderView = [[EGORefreshTableHeaderView alloc] initWithFrame:CGRectMake(0.0f, 0.0f - _MyTableView.bounds.size.height, self.view.frame.size.width, _MyTableView.bounds.size.height) IsHeader: YES];
_refreshHeaderView.delegate = self;
[_MyTableView addSubview:_refreshHeaderView];
}
[_refreshHeaderView refreshLastUpdatedDate];
if (_refreshFooterView == nil) {
_refreshFooterView = [[EGORefreshTableHeaderView alloc] initWithFrame:
CGRectMake(0, -1000, self.view.frame.size.width, _MyTableView.bounds.size.height) IsHeader: NO];
_refreshFooterView.delegate = self;
[_MyTableView addSubview:_refreshFooterView];
[self PutFooterAtEnd];//调整footer的位置
}
[_refreshFooterView refreshLastUpdatedDate];
}
好了,运行一下,我们可以看到表头表尾已经有刷新控件了,但是好像不起作用。
为什么刷新不工作呢?呵呵,我们漏了一个步骤:需要处理滚动条的滚动响应函数,加上以下代码:
#pragma mark -
#pragma mark UIScrollViewDelegate Methods
//滚动响应,当用户在滚动视图中拉动的时候就会被触发(这里是指table中拉动)
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
[_refreshHeaderView egoRefreshScrollViewDidScroll:scrollView];
[_refreshFooterView egoRefreshScrollViewDidScroll:scrollView];
// NSLog(@"scrollViewDidScroll\n");
}
//告诉代理,滚动视图中的拖拉动作结束了
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
[_refreshHeaderView egoRefreshScrollViewDidEndDragging:scrollView];
[_refreshFooterView egoRefreshScrollViewDidEndDragging:scrollView];
}
ok,现在就行了,再运行一下,发现可以刷新数据了,这个是将表头往下拉的情况
再试试将表尾往上拉:
嘿嘿,也成功了。到这里,我们就成功地加上了上下刷新效果。这里是个简单的例子,其实大多数代码都是差不多的,除了数据刷新部分。数据刷新看每个项目的情况的,只要在
GetMoreHeaderData和GetMoreFooterData里面更改就可以了。
总结
上下刷新的使用其实也是蛮简单的(怎么实现的刷新,这里不做探讨),这里画的图描述一下使用的基本步骤。
注意,数据生成部分一般都是起一个线程的,主线程用于ui显示,当数据生成结束的时候调用一把主线程来更新table
好了,简单介绍了一下上下刷新的使用,代码供参考: