上一个文章介绍了如何给table加上上下刷新功能,http://blog.csdn.net/zj510/article/details/8446833
假如我们在一个app里面有多处地方需要加上刷新功能,那么就会产生很多重复代码,因为有些代码是不变的。这样很不爽。有必要对他进行一些封装。
这里,就是尝试做一个简单的封装。
UITableView的子类:KRefreshTableView
通过xcode创建一个UITableView的子类,这个相当easy,我想ios开发者都会。
这个子类会实现刷新功能,为了有比较好的灵活性,我这里多加了一个回调,这个回调是用来数据更新的,因为大多数情况下,数据变化是挺大的,那么数据更新就由caller来指定吧。代码如下:
// // KRefreshTableView.h // DragList // // Created by Kevin on 12-12-28. // Copyright (c) 2012年 Kevin. All rights reserved. // #import <UIKit/UIKit.h> #import "EGORefreshTableHeaderView.h" @protocol DataUpdateCallback <NSObject>//定义一个回调,数据更新由调用者提供 @required - (int) UpdateData: (BOOL) header; @end @interface KRefreshTableView : UITableView <EGORefreshTableHeaderDelegate> { EGORefreshTableHeaderView *_refreshHeaderView;//表头刷新 EGORefreshTableHeaderView *_refreshFooterView;//表尾刷新 BOOL _reloading; } @property(retain, nonatomic) id<DataUpdateCallback> MyDelegate;//调用者,通过这个调用caller提供的数据更新回调 @property(retain, nonatomic) id Items;//指向调用者的数据,通常是一个NSMutableArray,可以得到一些数据信息 - (void) InitTable: (id) TableItems;//初始化 - (void) MyScrollViewDidScroll:(UIScrollView *)scrollView; - (void) MyScrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate; @end可以看到我里面加了一个DataUpdateCallback的协议,里面需要实现一个函数UpdateData。也就是说KRefreshTableView的调用者需要实现这个协议。
KRefreshTableView的具体实现代码就不介绍了。直接给出代码:
// // KRefreshTableView.m // DragList // // Created by Kevin on 12-12-28. // Copyright (c) 2012年 Kevin. All rights reserved. // #import "KRefreshTableView.h" @implementation KRefreshTableView - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // Initialization code } return self; } - (void)dealloc { [self.MyDelegate release]; [self.Items release]; [super dealloc]; } #pragma mark -- 处理下拉,上拉效果 -------------------------------- //将footer放到table的最后面 - (void) PutFooterAtEnd { CGRect footerFrame = _refreshFooterView.frame; CGRect r = CGRectMake(footerFrame.origin.x, self.contentSize.height, self.frame.size.width, footerFrame.size.height); if (r.origin.y < self.frame.size.height) { r.origin.y = self.frame.size.height; } _refreshFooterView.frame = r; } - (void) ShowHeaderAndFooter { if (_refreshHeaderView == nil) { _refreshHeaderView = [[[EGORefreshTableHeaderView alloc] initWithFrame:CGRectMake(0.0f, 0.0f - self.bounds.size.height, self.frame.size.width, self.bounds.size.height) IsHeader: YES] autorelease]; _refreshHeaderView.delegate = self;//设置代理 [self addSubview:_refreshHeaderView];//将刷新控件当作UITableView的子控件 } [_refreshHeaderView refreshLastUpdatedDate]; if (_refreshFooterView == nil) { _refreshFooterView = [[[EGORefreshTableHeaderView alloc] initWithFrame: CGRectMake(0, -1000, self.frame.size.width, self.bounds.size.height) IsHeader: NO] autorelease]; _refreshFooterView.delegate = self; [self addSubview:_refreshFooterView];//将刷新控件当作UITableView的子控件 [self PutFooterAtEnd];//调整footer的位置 } [_refreshFooterView refreshLastUpdatedDate]; } //header下拉处理线程结束会调用这个函数 - (void) FinishedLoadMoreHeaderData: (NSNumber*) num { //重新装载数据 [self reloadData]; [self PutFooterAtEnd]; [_refreshHeaderView refreshLastUpdatedDate];//修改最后更新时间 [_refreshHeaderView egoRefreshScrollViewDataSourceDidFinishedLoading:self]; _reloading = NO; } //footer上拉处理线程结束会调用这个函数 - (void) FinishedLoadMoreFooterData: (NSNumber*) num { [self reloadData];//重新装载数据 [self PutFooterAtEnd];//将footer放到table的最后 //将滚动条定位到上拉前的那一条item NSMutableArray* ary = self.Items;//table绑定的数据源 int prevNum = [ary count] - num.intValue;//得到更新前的行数 if (prevNum >= 1) { NSIndexPath* row = [NSIndexPath indexPathForRow:prevNum - 1 inSection:0]; //将table定位到更新时的位置 [self scrollToRowAtIndexPath:row atScrollPosition:UITableViewScrollPositionNone animated:NO]; } [num release]; [_refreshFooterView refreshLastUpdatedDate]; _reloading = NO; [_refreshFooterView egoRefreshScrollViewDataSourceDidFinishedLoading:self]; } //模拟一些数据,用户将header往下拉 - (void) GetMoreHeaderData { @autoreleasepool { [self.MyDelegate UpdateData:YES];//调用caller的数据更新回调,这样caller就可以设定需要更新的数据 //更新数据完毕,在主线程里面更新ui [self performSelectorOnMainThread:@selector(FinishedLoadMoreHeaderData:) withObject:nil waitUntilDone:YES]; } } //用户将footer往上拉的时候,放一些模拟数据 - (void) GetMoreFooterData { @autoreleasepool { int ret = [self.MyDelegate UpdateData:NO];//获取增加的行数 NSNumber* number = [NSNumber numberWithInt:ret]; [number retain];//主线程需要release [self performSelectorOnMainThread:@selector(FinishedLoadMoreFooterData:) withObject:number waitUntilDone:YES]; } } #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; // 当前是否有刷新任务在运行, } - (NSDate*)egoRefreshTableHeaderDataSourceLastUpdated:(EGORefreshTableHeaderView*)view{ return [NSDate date]; // 返回当前时间,这个时间会显示在更新时间 } #pragma mark - #pragma mark UIScrollViewDelegate Methods //下面2个函数会被caller调用(caller的滚动响应函数里面调用) //滚动响应,当用户在滚动视图中拉动的时候就会被触发(这里是指table中拉动) - (void) MyScrollViewDidScroll:(UIScrollView *)scrollView{ [_refreshHeaderView egoRefreshScrollViewDidScroll:scrollView]; [_refreshFooterView egoRefreshScrollViewDidScroll:scrollView]; // NSLog(@"scrollViewDidScroll\n"); } //告诉代理,滚动视图中的拖拉动作结束了 - (void) MyScrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ [_refreshHeaderView egoRefreshScrollViewDidEndDragging:scrollView]; [_refreshFooterView egoRefreshScrollViewDidEndDragging:scrollView]; } - (void) InitTable: (id) TableItems { _reloading = NO; [self setItems:TableItems]; [self ShowHeaderAndFooter];//创建表头表尾刷新控件 } /* // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during animation. - (void)drawRect:(CGRect)rect { // Drawing code } */ @end
如何使用
怎么使用呢?很简单,跟使用标准table基本一样。
先拖一个UITableView到xib,然后指定class为KRefreshTableView,如图
然后在xib对应的controller的ViewDidLoad里面调用:
//初始化 [_MyTableView InitTable: aryItems]; //设置UITableView的代理 _MyTableView.delegate = self; [_MyTableView setDataSource:self]; //将当前对象传给KRefreshTableView,这样KRefreshTableView可以回调数据更新函数 [_MyTableView setMyDelegate:self];
//数据更新回调,KRefreshTableView的调用者需要实现协议:DataUpdateCallback, //在协议函数UpdateData里面更新数据 - (int) UpdateData:(BOOL)header { if (header) { for (int i = 4; i > 0; i--) { NSString* str = [NSString stringWithFormat:@"item header %d", i]; [aryItems insertObject:str atIndex:0]; } return 4; } else { for (int i = 1; i < 10; i++) { NSString* str = [NSString stringWithFormat:@"item footer %d", i]; [aryItems addObject:str]; } return 9; } }这里模拟了一些数据。
然后在滚动响应函数里面调用一下KRefreshTableView函数,如下:
#pragma mark - #pragma mark UIScrollViewDelegate Methods //滚动响应,当用户在滚动视图中拉动的时候就会被触发(这里是指table中拉动) - (void)scrollViewDidScroll:(UIScrollView *)scrollView{ [_MyTableView MyScrollViewDidScroll:scrollView]; // NSLog(@"scrollViewDidScroll\n"); } //告诉代理,滚动视图中的拖拉动作结束了 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ [_MyTableView MyScrollViewDidEndDragging:scrollView willDecelerate:decelerate]; }
KViewController.h
// // KViewController.h // DragList // // Created by Kevin on 12-12-27. // Copyright (c) 2012年 Kevin. All rights reserved. // #import <UIKit/UIKit.h> #import "EGORefreshTableHeaderView.h" #import "KRefreshTableView.h" @interface KViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, DataUpdateCallback> { NSMutableArray* aryItems; } @property (retain, nonatomic) IBOutlet KRefreshTableView *MyTableView; @end
// // KViewController.m // DragList // // Created by Kevin on 12-12-27. // Copyright (c) 2012年 Kevin. All rights reserved. // #import "KViewController.h" #define ITEM_HEIGHT 50 @interface KViewController () @end @implementation KViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //模拟一些数据 aryItems = [NSMutableArray arrayWithCapacity:0]; [aryItems retain]; for (int i = 0; i < 5; i++) { NSString* str = [NSString stringWithFormat:@"item %d", i]; [aryItems addObject:str]; } //初始化 [_MyTableView InitTable: aryItems]; //设置UITableView的代理 _MyTableView.delegate = self; [_MyTableView setDataSource:self]; //将当前对象传给KRefreshTableView,这样KRefreshTableView可以回调数据更新函数 [_MyTableView setMyDelegate:self]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (void)dealloc { [_MyTableView release]; [aryItems release]; [super dealloc]; } - (void)viewDidUnload { [self setMyTableView:nil]; [super viewDidUnload]; } //数据更新回调,KRefreshTableView的调用者需要实现协议:DataUpdateCallback, //在协议函数UpdateData里面更新数据 - (int) UpdateData:(BOOL)header { if (header) { for (int i = 4; i > 0; i--) { NSString* str = [NSString stringWithFormat:@"item header %d", i]; [aryItems insertObject:str atIndex:0]; } return 4; } else { for (int i = 1; i < 10; i++) { NSString* str = [NSString stringWithFormat:@"item footer %d", i]; [aryItems addObject:str]; } return 9; } } #pragma mark -- table view delegate - (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return 50; } #pragma mark -- DataSource - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [aryItems count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"MyTableViewCell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { NSArray *nib = [[NSBundle mainBundle]loadNibNamed:@"TableViewCell" owner:self options:nil]; cell = [nib objectAtIndex:0]; } if (cell) { UILabel* label = (UILabel*)[cell viewWithTag:1]; NSString* item = [aryItems objectAtIndex: indexPath.row]; [label setText:item]; } return cell; } #pragma mark - #pragma mark UIScrollViewDelegate Methods //滚动响应,当用户在滚动视图中拉动的时候就会被触发(这里是指table中拉动) - (void)scrollViewDidScroll:(UIScrollView *)scrollView{ [_MyTableView MyScrollViewDidScroll:scrollView]; // NSLog(@"scrollViewDidScroll\n"); } //告诉代理,滚动视图中的拖拉动作结束了 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ [_MyTableView MyScrollViewDidEndDragging:scrollView willDecelerate:decelerate]; } @end
整个工程例子,使用xcode4.5,
http://download.csdn.net/detail/zj510/4938673