现在的位置: 首页 > 综合 > 正文

列表下拉/上拉刷新:(二)支持下拉/上拉的ViewController基类

2013年12月06日 ⁄ 综合 ⁄ 共 10411字 ⁄ 字号 评论关闭

目标:定义包含tableView的rootViewController类,其它需要此功能的ViewController可以直接继承,通过重载开始刷新数据的函数,执行自己实际的刷新和加载操作,其他关于footer/header复位、滚动等操作交给基类处理。

头文件:

#import "EGORefreshTableHeaderView.h"
#import "EGORefreshTableFooterView.h"
#import "EGOViewCommon.h"

@interface RootViewController : UIViewController  <EGORefreshTableDelegate, UITableViewDelegate, UITableViewDataSource>{
	
	EGORefreshTableHeaderView *_refreshHeaderView;
    EGORefreshTableFooterView *_refreshFooterView;
    
    UITableView *_tableView;
	
	//  Reloading var should really be your tableviews datasource
	//  Putting it here for demo purposes 
	BOOL _reloading;
}

@property(nonatomic, retain)UITableView *tableView;

// create/remove footer/header view, reset the position of the footer/header views
-(void)setFooterView;
-(void)removeFooterView;
-(void)createHeaderView;
-(void)removeHeaderView;

// overide methods
-(void)beginToReloadData:(EGORefreshPos)aRefreshPos;
-(void)finishReloadingData;

@end

实现:

#import "RootViewController.h"

@interface RootViewController (Private)
-(void)initTableViewWithRect:(CGRect)aRect;

@end

@implementation RootViewController
@synthesize tableView = _tableView;

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // create the tableview
    [self initTableViewWithRect:CGRectMake(self.view.bounds.origin.x, 
                                           self.view.bounds.origin.y, 
                                           self.view.frame.size.width, 
                                           self.view.frame.size.height-44.0)];
}

-(void)initTableViewWithRect:(CGRect)aRect{
    _tableView = [[UITableView alloc] initWithFrame:aRect  style:UITableViewStylePlain];
    _tableView.delegate = self;
    _tableView.dataSource = self;
    _tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
    _tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
    _tableView.backgroundColor = [UIColor clearColor];
    [self.view addSubview: _tableView];
    [_tableView release]; 
}

#pragma mark
#pragma methods for creating and removing the header view

-(void)createHeaderView{
    if (_refreshHeaderView && [_refreshHeaderView superview]) {
        [_refreshHeaderView removeFromSuperview];
    }
	_refreshHeaderView = [[EGORefreshTableHeaderView alloc] initWithFrame:
                               CGRectMake(0.0f, 0.0f - self.view.bounds.size.height,
                                          self.view.frame.size.width, self.view.bounds.size.height)];
    _refreshHeaderView.delegate = self;
    
	[_tableView addSubview:_refreshHeaderView];
    
    [_refreshHeaderView refreshLastUpdatedDate];
}

-(void)removeHeaderView{
    if (_refreshHeaderView && [_refreshHeaderView superview]) {
        [_refreshHeaderView removeFromSuperview];
    }
    _refreshHeaderView = nil;
}

-(void)setFooterView{
    // if the footerView is nil, then create it, reset the position of the footer
    CGFloat height = MAX(_tableView.contentSize.height, _tableView.frame.size.height);
    if (_refreshFooterView && [_refreshFooterView superview]) {
        // reset position
        _refreshFooterView.frame = CGRectMake(0.0f, 
                                              height, 
                                              _tableView.frame.size.width, 
                                              self.view.bounds.size.height);
    }else {
        // create the footerView
        _refreshFooterView = [[EGORefreshTableFooterView alloc] initWithFrame:
                               CGRectMake(0.0f, height,
                                          _tableView.frame.size.width, self.view.bounds.size.height)];
        _refreshFooterView.delegate = self;
        [_tableView addSubview:_refreshFooterView]; 
    }
    
    if (_refreshFooterView) {
        [_refreshFooterView refreshLastUpdatedDate];
    }
}

-(void)removeFooterView{
    if (_refreshFooterView && [_refreshFooterView superview]) {
        [_refreshFooterView removeFromSuperview];
    }
    _refreshFooterView = nil;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
	return YES;
}

#pragma mark-
#pragma mark force to show the refresh headerView
-(void)showRefreshHeader:(BOOL)animated{
	if (animated)
	{
		[UIView beginAnimations:nil context:NULL];
		[UIView setAnimationDuration:0.2];
		self.tableView.contentInset = UIEdgeInsetsMake(60.0f, 0.0f, 0.0f, 0.0f);
        // scroll the table view to the top region
        [self.tableView scrollRectToVisible:CGRectMake(0, 0.0f, 1, 1) animated:NO];	
        [UIView commitAnimations];
	}
	else
	{
		self.tableView.contentInset = UIEdgeInsetsMake(60.0f, 0.0f, 0.0f, 0.0f);
		[self.tableView scrollRectToVisible:CGRectMake(0, 0.0f, 1, 1) animated:NO];
	}

}

#pragma mark -
#pragma mark overide UITableViewDataSource methods

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 0;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 0;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    static NSString *CellIdentifier = @"Cell";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }
    
	// Configure the cell.

    return cell;
}

#pragma mark -
#pragma mark data reloading methods that must be overide by the subclass

-(void)beginToReloadData:(EGORefreshPos)aRefreshPos{
	
	//  should be calling your tableviews data source model to reload
	_reloading = YES;
    
	// overide, the actual loading data operation is done in the subclass
}

#pragma mark -
#pragma mark method that should be called when the refreshing is finished
- (void)finishReloadingData{
	
	//  model should call this when its done loading
	_reloading = NO;
    
	if (_refreshHeaderView) {
        [_refreshHeaderView egoRefreshScrollViewDataSourceDidFinishedLoading:_tableView];
    }
    
    if (_refreshFooterView) {
        [_refreshFooterView egoRefreshScrollViewDataSourceDidFinishedLoading:_tableView];
        [self setFooterView];
    }
    
    // overide, the actula reloading tableView operation and reseting position operation is done in the subclass
}

#pragma mark -
#pragma mark UIScrollViewDelegate Methods

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{	
	if (_refreshHeaderView) {
        [_refreshHeaderView egoRefreshScrollViewDidScroll:scrollView];
    }
	
	if (_refreshFooterView) {
        [_refreshFooterView egoRefreshScrollViewDidScroll:scrollView];
    }	
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
	if (_refreshHeaderView) {
        [_refreshHeaderView egoRefreshScrollViewDidEndDragging:scrollView];
    }
	
	if (_refreshFooterView) {
        [_refreshFooterView egoRefreshScrollViewDidEndDragging:scrollView];
    }
}


#pragma mark -
#pragma mark EGORefreshTableDelegate Methods

- (void)egoRefreshTableDidTriggerRefresh:(EGORefreshPos)aRefreshPos{
	
	[self beginToReloadData:aRefreshPos];
	
}

- (BOOL)egoRefreshTableDataSourceIsLoading:(UIView*)view{
	
	return _reloading; // should return if data source model is reloading
	
}


// if we don't realize this method, it won't display the refresh timestamp
- (NSDate*)egoRefreshTableDataSourceLastUpdated:(UIView*)view{
	
	return [NSDate date]; // should return date data source was last changed
	
}


#pragma mark -
#pragma mark Memory Management

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

- (void)viewDidUnload {
    SAFE_RELEASE(_refreshHeaderView)
    SAFE_RELEASE(_refreshFooterView)
}

- (void)dealloc {
    [super dealloc];
}

具体使用:

可以在一加载完view就createHeaderView,因为它总是在列表的最顶部,位置不会变化。不想要的时候remove。

footerView要在[tableView reloadData]之后才能设置,因为footer的位置取决于tableView的ContentSize。一般来讲:加载第一页数据之前,不需要创建footer,等到第一页的数据加载完成之后调用setFooterView(如果不存footer,会创建,否则重置位置),来首次创建footer,后续加载第二页、第三页...的时候,也是加载完成reloadData,之后setFooterView重新调整其位置。

重载-(void)beginToReloadData:(EGORefreshPos)aRefreshPos,具体实现你想做的事情,例如:

#pragma mark-
#pragma mark overide methods
-(void)beginToReloadData:(EGORefreshPos)aRefreshPos{
	[super beginToReloadData:aRefreshPos];
    
    if (aRefreshPos == EGORefreshHeader) {
        // pull down to refresh data
        [self performSelector:@selector(testRealRefreshDataSource) withObject:nil afterDelay:2.0];
    }else if(aRefreshPos == EGORefreshFooter){
        // pull up to load more data
        [self performSelector:@selector(testRealLoadMoreData) withObject:nil afterDelay:2.0];
    }
}

如上,可以在这里触发联网操作获取数据,我用了延时操作来模拟而已。

给出我的完整的测试界面代码

示例:

#import "MyTestViewController.h"

@interface MyTestViewController ()

@end

@implementation MyTestViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view.
    
    // test data
    _totalNumberOfRows = 100;
    _refreshCount = 0;
    _dataSource = [[NSMutableArray alloc] initWithCapacity:4];

    // set header
    [self createHeaderView];
    
    // the footer should be set after the data of tableView has been loaded, the frame of footer is according to the contentSize of tableView
    // here, actually begin too load your data, eg: from the netserver

    [self performSelector:@selector(testFinishedLoadData) withObject:nil afterDelay:2.0f];
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

#pragma mark -
#pragma mark overide UITableViewDataSource methods

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return _dataSource?1:0;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return _dataSource?_dataSource.count:0;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    static NSString *CellIdentifier = @"Cell";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }
    
	// Configure the cell.
    if (_dataSource && indexPath.row < _dataSource.count) {
        cell.textLabel.text = [_dataSource objectAtIndex:indexPath.row];
    }
    
    return cell;
}

#pragma mark-
#pragma mark overide methods
-(void)beginToReloadData:(EGORefreshPos)aRefreshPos{
	[super beginToReloadData:aRefreshPos];
    
    if (aRefreshPos == EGORefreshHeader) {
        // pull down to refresh data
        [self performSelector:@selector(testRealRefreshDataSource) withObject:nil afterDelay:2.0];
    }else if(aRefreshPos == EGORefreshFooter){
        // pull up to load more data
        [self performSelector:@selector(testRealLoadMoreData) withObject:nil afterDelay:2.0];
    }
}

-(void)testRealRefreshDataSource{
    NSInteger count = _dataSource?_dataSource.count:0;
    
    [_dataSource removeAllObjects];
    _refreshCount ++;
    
    for (int i = 0; i < count; i++) {
        NSString *newString = [NSString stringWithFormat:@"%d_new label number %d", _refreshCount,i];
        [_dataSource addObject:newString];
    }
    
    // after refreshing data, call finishReloadingData to reset the header/footer view
    [_tableView reloadData];
    [self finishReloadingData];
}

-(void)testRealLoadMoreData{
    NSInteger count = _dataSource?_dataSource.count:0;
    NSString *stringFormat;
    if (_refreshCount == 0) {
        stringFormat = @"label number %d";
    }else {
        stringFormat = [NSString stringWithFormat:@"%d_new label number ", _refreshCount];
        stringFormat = [stringFormat stringByAppendingString:@"%d"];
    }
    
    for (int i = 0; i < 20; i++) {
        NSString *newString = [NSString stringWithFormat:stringFormat, i+count];
        if (_dataSource == nil) {
            _dataSource = [[NSMutableArray alloc] initWithCapacity:4];

        }
        [_dataSource addObject:newString];
    }
    
    _loadMoreCount ++;
    
    // after refreshing data, call finishReloadingData to reset the header/footer view
    [_tableView reloadData];
    [self finishReloadingData];
}

-(void)testFinishedLoadData{
    for (int i = 0; i < 20; i++) {
        NSString *tableString = [NSString stringWithFormat:@"label number %d", i];
        [_dataSource addObject:tableString];
    }
    
    // after loading data, should reloadData and set the footer to the proper position
    [self.tableView reloadData];
    [self setFooterView];
}

注:因为还要做一些进一步的加工,例如强制刷新等,所以代码还没写完,例如一进来的延迟2秒加载数据,感觉很奇怪,其实是为了后续要写的东西预留的,凑合看吧。

【上篇】
【下篇】

抱歉!评论已关闭.