上一个文章介绍了如何给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
ok,以上就是带刷新功能的Table的所有代码。
如何使用
怎么使用呢?很简单,跟使用标准table基本一样。
先拖一个UITableView到xib,然后指定class为KRefreshTableView,如图
然后在xib对应的controller的ViewDidLoad里面调用:
//初始化
[_MyTableView InitTable: aryItems];
//设置UITableView的代理
_MyTableView.delegate = self;
[_MyTableView setDataSource:self];
//将当前对象传给KRefreshTableView,这样KRefreshTableView可以回调数据更新函数
[_MyTableView setMyDelegate:self];
实现DataUpdateCallback协议函数UpdateData,如下:
//数据更新回调,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];
}
其他的代码基本跟标准table一样,贴出所有代码:
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:
//
// 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
运行一下,就可以看到带上下刷新的table了。这样只要需要刷新功能的地方,就可以用KRefreshTableView这个类代替标准的UITableView了。
整个工程例子,使用xcode4.5,