分享

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

 叹落花 2015-01-17

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


头文件:

  1. #import "EGORefreshTableHeaderView.h"  
  2. #import "EGORefreshTableFooterView.h"  
  3. #import "EGOViewCommon.h"  
  4.   
  5. @interface RootViewController : UIViewController  <EGORefreshTableDelegate, UITableViewDelegate, UITableViewDataSource>{  
  6.       
  7.     EGORefreshTableHeaderView *_refreshHeaderView;  
  8.     EGORefreshTableFooterView *_refreshFooterView;  
  9.       
  10.     UITableView *_tableView;  
  11.       
  12.     //  Reloading var should really be your tableviews datasource  
  13.     //  Putting it here for demo purposes   
  14.     BOOL _reloading;  
  15. }  
  16.   
  17. @property(nonatomic, retain)UITableView *tableView;  
  18.   
  19. // create/remove footer/header view, reset the position of the footer/header views  
  20. -(void)setFooterView;  
  21. -(void)removeFooterView;  
  22. -(void)createHeaderView;  
  23. -(void)removeHeaderView;  
  24.   
  25. // overide methods  
  26. -(void)beginToReloadData:(EGORefreshPos)aRefreshPos;  
  27. -(void)finishReloadingData;  
  28.   
  29. @end  


实现:

  1. #import "RootViewController.h"  
  2.   
  3. @interface RootViewController (Private)  
  4. -(void)initTableViewWithRect:(CGRect)aRect;  
  5.   
  6. @end  
  7.   
  8. @implementation RootViewController  
  9. @synthesize tableView = _tableView;  
  10.   
  11. - (void)viewDidLoad {  
  12.     [super viewDidLoad];  
  13.       
  14.     // create the tableview  
  15.     [self initTableViewWithRect:CGRectMake(self.view.bounds.origin.x,   
  16.                                            self.view.bounds.origin.y,   
  17.                                            self.view.frame.size.width,   
  18.                                            self.view.frame.size.height-44.0)];  
  19. }  
  20.   
  21. -(void)initTableViewWithRect:(CGRect)aRect{  
  22.     _tableView = [[UITableView alloc] initWithFrame:aRect  style:UITableViewStylePlain];  
  23.     _tableView.delegate = self;  
  24.     _tableView.dataSource = self;  
  25.     _tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;  
  26.     _tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth;  
  27.     _tableView.backgroundColor = [UIColor clearColor];  
  28.     [self.view addSubview: _tableView];  
  29.     [_tableView release];   
  30. }  
  31.   
  32. #pragma mark  
  33. #pragma methods for creating and removing the header view  
  34.   
  35. -(void)createHeaderView{  
  36.     if (_refreshHeaderView && [_refreshHeaderView superview]) {  
  37.         [_refreshHeaderView removeFromSuperview];  
  38.     }  
  39.     _refreshHeaderView = [[EGORefreshTableHeaderView alloc] initWithFrame:  
  40.                                CGRectMake(0.0f, 0.0f - self.view.bounds.size.height,  
  41.                                           self.view.frame.size.width, self.view.bounds.size.height)];  
  42.     _refreshHeaderView.delegate = self;  
  43.       
  44.     [_tableView addSubview:_refreshHeaderView];  
  45.       
  46.     [_refreshHeaderView refreshLastUpdatedDate];  
  47. }  
  48.   
  49. -(void)removeHeaderView{  
  50.     if (_refreshHeaderView && [_refreshHeaderView superview]) {  
  51.         [_refreshHeaderView removeFromSuperview];  
  52.     }  
  53.     _refreshHeaderView = nil;  
  54. }  
  55.   
  56. -(void)setFooterView{  
  57.     // if the footerView is nil, then create it, reset the position of the footer  
  58.     CGFloat height = MAX(_tableView.contentSize.height, _tableView.frame.size.height);  
  59.     if (_refreshFooterView && [_refreshFooterView superview]) {  
  60.         // reset position  
  61.         _refreshFooterView.frame = CGRectMake(0.0f,   
  62.                                               height,   
  63.                                               _tableView.frame.size.width,   
  64.                                               self.view.bounds.size.height);  
  65.     }else {  
  66.         // create the footerView  
  67.         _refreshFooterView = [[EGORefreshTableFooterView alloc] initWithFrame:  
  68.                                CGRectMake(0.0f, height,  
  69.                                           _tableView.frame.size.width, self.view.bounds.size.height)];  
  70.         _refreshFooterView.delegate = self;  
  71.         [_tableView addSubview:_refreshFooterView];   
  72.     }  
  73.       
  74.     if (_refreshFooterView) {  
  75.         [_refreshFooterView refreshLastUpdatedDate];  
  76.     }  
  77. }  
  78.   
  79. -(void)removeFooterView{  
  80.     if (_refreshFooterView && [_refreshFooterView superview]) {  
  81.         [_refreshFooterView removeFromSuperview];  
  82.     }  
  83.     _refreshFooterView = nil;  
  84. }  
  85.   
  86. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {  
  87.     return YES;  
  88. }  
  89.   
  90. #pragma mark-  
  91. #pragma mark force to show the refresh headerView  
  92. -(void)showRefreshHeader:(BOOL)animated{  
  93.     if (animated)  
  94.     {  
  95.         [UIView beginAnimations:nil context:NULL];  
  96.         [UIView setAnimationDuration:0.2];  
  97.         self.tableView.contentInset = UIEdgeInsetsMake(60.0f, 0.0f, 0.0f, 0.0f);  
  98.         // scroll the table view to the top region  
  99.         [self.tableView scrollRectToVisible:CGRectMake(0, 0.0f, 1, 1) animated:NO];   
  100.         [UIView commitAnimations];  
  101.     }  
  102.     else  
  103.     {  
  104.         self.tableView.contentInset = UIEdgeInsetsMake(60.0f, 0.0f, 0.0f, 0.0f);  
  105.         [self.tableView scrollRectToVisible:CGRectMake(0, 0.0f, 1, 1) animated:NO];  
  106.     }  
  107.   
  108. }  
  109.   
  110. #pragma mark -  
  111. #pragma mark overide UITableViewDataSource methods  
  112.   
  113. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {  
  114.     return 0;  
  115. }  
  116.   
  117. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {  
  118.     return 0;  
  119. }  
  120.   
  121. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {  
  122.       
  123.     static NSString *CellIdentifier = @"Cell";  
  124.       
  125.     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];  
  126.     if (cell == nil) {  
  127.         cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];  
  128.     }  
  129.       
  130.     // Configure the cell.  
  131.   
  132.     return cell;  
  133. }  
  134.   
  135. #pragma mark -  
  136. #pragma mark data reloading methods that must be overide by the subclass  
  137.   
  138. -(void)beginToReloadData:(EGORefreshPos)aRefreshPos{  
  139.       
  140.     //  should be calling your tableviews data source model to reload  
  141.     _reloading = YES;  
  142.       
  143.     // overide, the actual loading data operation is done in the subclass  
  144. }  
  145.   
  146. #pragma mark -  
  147. #pragma mark method that should be called when the refreshing is finished  
  148. - (void)finishReloadingData{  
  149.       
  150.     //  model should call this when its done loading  
  151.     _reloading = NO;  
  152.       
  153.     if (_refreshHeaderView) {  
  154.         [_refreshHeaderView egoRefreshScrollViewDataSourceDidFinishedLoading:_tableView];  
  155.     }  
  156.       
  157.     if (_refreshFooterView) {  
  158.         [_refreshFooterView egoRefreshScrollViewDataSourceDidFinishedLoading:_tableView];  
  159.         [self setFooterView];  
  160.     }  
  161.       
  162.     // overide, the actula reloading tableView operation and reseting position operation is done in the subclass  
  163. }  
  164.   
  165. #pragma mark -  
  166. #pragma mark UIScrollViewDelegate Methods  
  167.   
  168. - (void)scrollViewDidScroll:(UIScrollView *)scrollView{   
  169.     if (_refreshHeaderView) {  
  170.         [_refreshHeaderView egoRefreshScrollViewDidScroll:scrollView];  
  171.     }  
  172.       
  173.     if (_refreshFooterView) {  
  174.         [_refreshFooterView egoRefreshScrollViewDidScroll:scrollView];  
  175.     }     
  176. }  
  177.   
  178. - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{  
  179.     if (_refreshHeaderView) {  
  180.         [_refreshHeaderView egoRefreshScrollViewDidEndDragging:scrollView];  
  181.     }  
  182.       
  183.     if (_refreshFooterView) {  
  184.         [_refreshFooterView egoRefreshScrollViewDidEndDragging:scrollView];  
  185.     }  
  186. }  
  187.   
  188.   
  189. #pragma mark -  
  190. #pragma mark EGORefreshTableDelegate Methods  
  191.   
  192. - (void)egoRefreshTableDidTriggerRefresh:(EGORefreshPos)aRefreshPos{  
  193.       
  194.     [self beginToReloadData:aRefreshPos];  
  195.       
  196. }  
  197.   
  198. - (BOOL)egoRefreshTableDataSourceIsLoading:(UIView*)view{  
  199.       
  200.     return _reloading; // should return if data source model is reloading  
  201.       
  202. }  
  203.   
  204.   
  205. // if we don't realize this method, it won't display the refresh timestamp  
  206. - (NSDate*)egoRefreshTableDataSourceLastUpdated:(UIView*)view{  
  207.       
  208.     return [NSDate date]; // should return date data source was last changed  
  209.       
  210. }  
  211.   
  212.   
  213. #pragma mark -  
  214. #pragma mark Memory Management  
  215.   
  216. - (void)didReceiveMemoryWarning {  
  217.     [super didReceiveMemoryWarning];  
  218. }  
  219.   
  220. - (void)viewDidUnload {  
  221.     SAFE_RELEASE(_refreshHeaderView)  
  222.     SAFE_RELEASE(_refreshFooterView)  
  223. }  
  224.   
  225. - (void)dealloc {  
  226.     [super dealloc];  
  227. }  

具体使用:

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

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

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

  1. #pragma mark-  
  2. #pragma mark overide methods  
  3. -(void)beginToReloadData:(EGORefreshPos)aRefreshPos{  
  4.     [super beginToReloadData:aRefreshPos];  
  5.       
  6.     if (aRefreshPos == EGORefreshHeader) {  
  7.         // pull down to refresh data  
  8.         [self performSelector:@selector(testRealRefreshDataSource) withObject:nil afterDelay:2.0];  
  9.     }else if(aRefreshPos == EGORefreshFooter){  
  10.         // pull up to load more data  
  11.         [self performSelector:@selector(testRealLoadMoreData) withObject:nil afterDelay:2.0];  
  12.     }  
  13. }  

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


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

示例:

  1. #import "MyTestViewController.h"  
  2.   
  3. @interface MyTestViewController ()  
  4.   
  5. @end  
  6.   
  7. @implementation MyTestViewController  
  8.   
  9. - (void)viewDidLoad  
  10. {  
  11.     [super viewDidLoad];  
  12.     // Do any additional setup after loading the view.  
  13.       
  14.     // test data  
  15.     _totalNumberOfRows = 100;  
  16.     _refreshCount = 0;  
  17.     _dataSource = [[NSMutableArray alloc] initWithCapacity:4];  
  18.   
  19.     // set header  
  20.     [self createHeaderView];  
  21.       
  22.     // the footer should be set after the data of tableView has been loaded, the frame of footer is according to the contentSize of tableView  
  23.     // here, actually begin too load your data, eg: from the netserver  
  24.   
  25.     [self performSelector:@selector(testFinishedLoadData) withObject:nil afterDelay:2.0f];  
  26. }  
  27.   
  28. - (void)viewDidUnload  
  29. {  
  30.     [super viewDidUnload];  
  31.     // Release any retained subviews of the main view.  
  32. }  
  33.   
  34. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation  
  35. {  
  36.     return (interfaceOrientation == UIInterfaceOrientationPortrait);  
  37. }  
  38.   
  39. #pragma mark -  
  40. #pragma mark overide UITableViewDataSource methods  
  41.   
  42. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {  
  43.     return _dataSource?1:0;  
  44. }  
  45.   
  46. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{  
  47.     return _dataSource?_dataSource.count:0;  
  48. }  
  49.   
  50. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {  
  51.       
  52.     static NSString *CellIdentifier = @"Cell";  
  53.       
  54.     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];  
  55.     if (cell == nil) {  
  56.         cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];  
  57.     }  
  58.       
  59.     // Configure the cell.  
  60.     if (_dataSource && indexPath.row < _dataSource.count) {  
  61.         cell.textLabel.text = [_dataSource objectAtIndex:indexPath.row];  
  62.     }  
  63.       
  64.     return cell;  
  65. }  
  66.   
  67. #pragma mark-  
  68. #pragma mark overide methods  
  69. -(void)beginToReloadData:(EGORefreshPos)aRefreshPos{  
  70.     [super beginToReloadData:aRefreshPos];  
  71.       
  72.     if (aRefreshPos == EGORefreshHeader) {  
  73.         // pull down to refresh data  
  74.         [self performSelector:@selector(testRealRefreshDataSource) withObject:nil afterDelay:2.0];  
  75.     }else if(aRefreshPos == EGORefreshFooter){  
  76.         // pull up to load more data  
  77.         [self performSelector:@selector(testRealLoadMoreData) withObject:nil afterDelay:2.0];  
  78.     }  
  79. }  
  80.   
  81. -(void)testRealRefreshDataSource{  
  82.     NSInteger count = _dataSource?_dataSource.count:0;  
  83.       
  84.     [_dataSource removeAllObjects];  
  85.     _refreshCount ++;  
  86.       
  87.     for (int i = 0; i < count; i++) {  
  88.         NSString *newString = [NSString stringWithFormat:@"%d_new label number %d", _refreshCount,i];  
  89.         [_dataSource addObject:newString];  
  90.     }  
  91.       
  92.     // after refreshing data, call finishReloadingData to reset the header/footer view  
  93.     [_tableView reloadData];  
  94.     [self finishReloadingData];  
  95. }  
  96.   
  97. -(void)testRealLoadMoreData{  
  98.     NSInteger count = _dataSource?_dataSource.count:0;  
  99.     NSString *stringFormat;  
  100.     if (_refreshCount == 0) {  
  101.         stringFormat = @"label number %d";  
  102.     }else {  
  103.         stringFormat = [NSString stringWithFormat:@"%d_new label number ", _refreshCount];  
  104.         stringFormat = [stringFormat stringByAppendingString:@"%d"];  
  105.     }  
  106.       
  107.     for (int i = 0; i < 20; i++) {  
  108.         NSString *newString = [NSString stringWithFormat:stringFormat, i+count];  
  109.         if (_dataSource == nil) {  
  110.             _dataSource = [[NSMutableArray alloc] initWithCapacity:4];  
  111.   
  112.         }  
  113.         [_dataSource addObject:newString];  
  114.     }  
  115.       
  116.     _loadMoreCount ++;  
  117.       
  118.     // after refreshing data, call finishReloadingData to reset the header/footer view  
  119.     [_tableView reloadData];  
  120.     [self finishReloadingData];  
  121. }  
  122.   
  123. -(void)testFinishedLoadData{  
  124.     for (int i = 0; i < 20; i++) {  
  125.         NSString *tableString = [NSString stringWithFormat:@"label number %d", i];  
  126.         [_dataSource addObject:tableString];  
  127.     }  
  128.       
  129.     // after loading data, should reloadData and set the footer to the proper position  
  130.     [self.tableView reloadData];  
  131.     [self setFooterView];  
  132. }  

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


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多