目标:定义包含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秒加载数据,感觉很奇怪,其实是为了后续要写的东西预留的,凑合看吧。
|