天天看点

EGORefreshTableHeaderView 解读 代码解读 ELTableViewController 的使用 写在最后

查看源码。称它为ELTableViewController是取了EGORefreshTableHeaderView以及LoadMoreTableFooterView的首字母。

这份代码中包含了一个示例程序以及三个必备组件:

1、  EGORefreshTableHeaderView

2、  LoadMoreTableFooterView(修改版,原版不能适应任何尺寸的高度)

3、  Apple官方提供的异步下载UITableView中的图片的示例组件(IconDownLoader),这个只适用于下载类似于社交网络中的用户头像,不建议使用它来下载那些大图片,因为它甚至都没有缓存(如果图片很大,推荐使用SDImage)

代码解读

它已经内置实现了这些协议,所以在你使用它的时候,无需设置和实现。

[cpp]  view plain  copy  print ?

  1. @interface ELTableViewController : UIViewController  
  2. <  
  3. UITableViewDelegate,  
  4. UITableViewDataSource,  
  5. EGORefreshTableHeaderDelegate,  
  6. LoadMoreTableFooterDelegate,  
  7. IconDownloaderDelegate  
  8. >  

对于不断变化的业务逻辑,这里提供了所有需要实现的block:

[cpp]  view plain  copy  print ?

  1. //blocks for UITableView delegate  
  2. typedef UITableViewCell* (^cellForRowAtIndexPathDelegate) (UITableView *,NSIndexPath *);  
  3. typedef CGFloat (^heightForRowAtIndexPathDelegate) (UITableView *,NSIndexPath *);  
  4. typedef void (^didSelectRowAtIndexPathDelegate) (UITableView *,NSIndexPath *);  
  5. //blocks for refresh and load more  
  6. typedef void (^refreshDataSourceFunc) (void);  
  7. typedef void (^loadMoreDataSourceFunc) (void);  
  8. typedef void (^refreshDataSourceCompleted) (void);  
  9. typedef void (^loadMoreDataSourceCompleted) (void);  
  10. //use to load image (async)  
  11. typedef void (^loadImagesForVisiableRowsFunc) (void);  
  12. typedef void (^appImageDownloadCompleted) (NSIndexPath *);  

它们以属性的形式对外公开:

[cpp]  view plain  copy  print ?

  1. //property for blocks  
  2. @property (nonatomic,copy) cellForRowAtIndexPathDelegate cellForRowAtIndexPathDelegate;  
  3. @property (nonatomic,copy) heightForRowAtIndexPathDelegate heightForRowAtIndexPathDelegate;  
  4. @property (nonatomic,copy) didSelectRowAtIndexPathDelegate didSelectRowAtIndexPathDelegate;  
  5. @property (nonatomic,copy) loadMoreDataSourceFunc loadMoreDataSourceFunc;  
  6. @property (nonatomic,copy) refreshDataSourceFunc refreshDataSourceFunc;  
  7. @property (nonatomic,copy) refreshDataSourceCompleted refreshDataSourceCompleted;  
  8. @property (nonatomic,copy) loadMoreDataSourceCompleted loadMoreDataSourceCompleted;  
  9. @property (nonatomic,copy) loadImagesForVisiableRowsFunc loadImagesForVisiableRowsFunc;  
  10. @property (nonatomic,copy) appImageDownloadCompleted appImageDownloadCompleted;  

对于上提加载更多、下拉刷新、图片异步加载这几个功能都是可选的,它们以组件的形式存在。比如,在实例化该controller的时候你就可以设置上提和下拉是否可用。而对于图片下载,你只要不实现其相应得block,它也不会对你造成额外的负担。

[cpp]  view plain  copy  print ?

  1. - (id)initWithRefreshHeaderViewEnabled:(BOOL)enableRefreshHeaderView   
  2. andLoadMoreFooterViewEnabled:(BOOL)enableLoadMoreFooterView;  
  3. - (id)initWithRefreshHeaderViewEnabled:(BOOL)enableRefreshHeaderView   
  4.           andLoadMoreFooterViewEnabled:(BOOL)enableLoadMoreFooterView   
  5.                      andTableViewFrame:(CGRect)frame;  

[cpp]  view plain  copy  print ?

  1. #pragma mark - UITableView Delegate -  
  2. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{  
  3.     if (nil==self.dataSource) {  
  4.         return 0;  
  5.     }  
  6.     return [self.dataSource count];  
  7. }  
  8. - (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{  
  9.     if (!self.cellForRowAtIndexPathDelegate) {  
  10.         @throw [NSException exceptionWithName:@"Framework Error"   
  11.                                        reason:@"Must be setting cellForRowAtIndexPathBlock for UITableView" userInfo:nil];  
  12.     }  
  13.     return self.cellForRowAtIndexPathDelegate(tableView,indexPath);  
  14. }  
  15. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{  
  16.     if (!self.heightForRowAtIndexPathDelegate) {  
  17.         @throw [NSException exceptionWithName:@"Framework Error"   
  18.                                        reason:@"Must be setting heightForRowAtIndexPathDelegate for UITableView" userInfo:nil];  
  19.     }  
  20.     return self.heightForRowAtIndexPathDelegate(tableView,indexPath);  
  21. }  
  22. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{  
  23.     if (self.didSelectRowAtIndexPathDelegate) {  
  24.         self.didSelectRowAtIndexPathDelegate(tableView,indexPath);  
  25.     }  
  26. }  
  27. #pragma mark - LoadMoreTableFooterDelegate Methods -  
  28. - (void)loadMoreTableFooterDidTriggerRefresh:(LoadMoreTableFooterView *)view{  
  29.     if (self.loadMoreDataSourceFunc&&self.loadMoreDataSourceCompleted) {  
  30.         self.loadMoreDataSourceFunc();  
  31.         double delayInSeconds = 3.0;  
  32.         dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);  
  33.         dispatch_after(popTime, dispatch_get_main_queue(),   
  34.                        self.loadMoreDataSourceCompleted);  
  35.     }  
  36. }  
  37. - (BOOL)loadMoreTableFooterDataSourceIsLoading:(LoadMoreTableFooterView *)view{  
  38.     return self.isLoadingMore;  
  39. }  
  40. #pragma mark - EGORefreshTableHeaderDelegate Methods -  
  41. -(void)egoRefreshTableHeaderDidTriggerRefresh:(EGORefreshTableHeaderView *)view{  
  42.     if (self.refreshDataSourceFunc&&self.refreshDataSourceCompleted){  
  43.         self.refreshDataSourceFunc();  
  44.         double delayInSeconds = 3.0;  
  45.         dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);  
  46.         dispatch_after(popTime, dispatch_get_main_queue(),   
  47.                        self.refreshDataSourceCompleted);  
  48.     }  
  49. }  
  50. -(BOOL)egoRefreshTableHeaderDataSourceIsLoading:(EGORefreshTableHeaderView *)view{  
  51.     return self.isRefreshing;  
  52. }  
  53. -(NSDate *)egoRefreshTableHeaderDataSourceLastUpdated:(EGORefreshTableHeaderView *)view{  
  54.     return [NSDate date];  
  55. }  
  56. #pragma mark - UIScrollViewDelegate Methods -  
  57. -(void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{  
  58.     self.currentOffsetPoint=scrollView.contentOffset;  
  59. }  
  60. -(void)scrollViewDidScroll:(UIScrollView *)scrollView{  
  61.     CGPoint pt=scrollView.contentOffset;  
  62.     if (self.currentOffsetPoint.y<pt.y) {  
  63.         [self.loadMoreFooterView loadMoreScrollViewDidScroll:scrollView];  
  64.     }else {       
  65.         [self.refreshHeaderView egoRefreshScrollViewDidScroll:scrollView];  
  66.     }  
  67. }  
  68. -(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{  
  69.     CGPoint pt=scrollView.contentOffset;  
  70.     if (self.currentOffsetPoint.y<pt.y) {  
  71.         [self.loadMoreFooterView loadMoreScrollViewDidEndDragging:scrollView];  
  72.     }else {  
  73.         [self.refreshHeaderView egoRefreshScrollViewDidEndDragging:scrollView];  
  74.     }  
  75.     if (!decelerate&&self.loadImagesForVisiableRowsFunc) {  
  76.         self.loadImagesForVisiableRowsFunc();  
  77.     }  
  78. }  
  79. -(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{  
  80.     if (self.loadImagesForVisiableRowsFunc) {  
  81.         self.loadImagesForVisiableRowsFunc();  
  82.     }  
  83. }  
  84. #pragma mark - download image async -  
  85. -(void)appImageDidLoad:(NSIndexPath *)indexPath{  
  86.     if (self.appImageDownloadCompleted) {  
  87.         self.appImageDownloadCompleted(indexPath);  
  88.     }  
  89. }  

ELTableViewController 的使用

创建一个新的controller继承自:ELTableViewController;

override父类的initBlocks方法:

[cpp]  view plain  copy  print ?

  1. #pragma mark - private methods -  
  2. - (void)loadDataSource{  
  3.     self.dataSource=[NSMutableArray array];  
  4.     [self.dataSource addObject:@"dataSource_1"];  
  5.     [self.dataSource addObject:@"dataSource_2"];  
  6.     [self.dataSource addObject:@"dataSource_3"];  
  7.     [self.dataSource addObject:@"dataSource_4"];  
  8.     [self.dataSource addObject:@"dataSource_5"];  
  9.     [self.dataSource addObject:@"dataSource_6"];  
  10.     [self.dataSource addObject:@"dataSource_7"];  
  11.     [self.dataSource addObject:@"dataSource_8"];  
  12.     [self.dataSource addObject:@"dataSource_9"];  
  13.     [self.dataSource addObject:@"dataSource_10"];  
  14. }  
  15. - (void)initBlocks{  
  16.     __block TestViewController *blockedSelf=self;  
  17.     //load more  
  18.     self.loadMoreDataSourceFunc=^{  
  19.         [blockedSelf.dataSource addObject:@"loadMoreDataSourceBlock_1"];  
  20.         [blockedSelf.dataSource addObject:@"loadMoreDataSourceBlock_2"];  
  21.         [blockedSelf.dataSource addObject:@"loadMoreDataSourceBlock_3"];  
  22.         [blockedSelf.dataSource addObject:@"loadMoreDataSourceBlock_4"];  
  23.         [blockedSelf.dataSource addObject:@"loadMoreDataSourceBlock_5"];  
  24.         blockedSelf.isLoadingMore=YES;  
  25.         [self.tableView reloadData];  
  26.         NSLog(@"loadMoreDataSourceBlock was invoked");  
  27.     };  
  28.     //load more completed  
  29.     self.loadMoreDataSourceCompleted=^{  
  30.         blockedSelf.isLoadingMore=NO;  
  31.         [blockedSelf.loadMoreFooterView loadMoreScrollViewDataSourceDidFinishedLoading:self.tableView];  
  32.         NSLog(@"after loadMore completed");  
  33.     };  
  34.     //refresh  
  35.     self.refreshDataSourceFunc=^{  
  36.         blockedSelf.dataSource=[NSMutableArray array];  
  37.         [blockedSelf.dataSource addObject:@"refreshDataSourceBlock_1"];  
  38.         [blockedSelf.dataSource addObject:@"refreshDataSourceBlock_2"];  
  39.         [blockedSelf.dataSource addObject:@"refreshDataSourceBlock_3"];  
  40.         [blockedSelf.dataSource addObject:@"refreshDataSourceBlock_4"];  
  41.         [blockedSelf.dataSource addObject:@"refreshDataSourceBlock_5"];  
  42.         blockedSelf.isRefreshing=YES;  
  43.         [self.tableView reloadData];  
  44.         NSLog(@"refreshDataSourceBlock was invoked");  
  45.     };  
  46.     //refresh completed  
  47.     self.refreshDataSourceCompleted=^{  
  48.         blockedSelf.isRefreshing=NO;  
  49.         [blockedSelf.loadMoreFooterView loadMoreScrollViewDataSourceDidFinishedLoading:self.tableView];  
  50.         NSLog(@"after refresh completed");  
  51.     };  
  52.     self.cellForRowAtIndexPathDelegate=^(UITableView *tableView, NSIndexPath *indexPath){  
  53.         static NSString *cellIdentifier=@"cellIdentifier";  
  54.         UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier];  
  55.         if (!cell) {  
  56.             cell=[[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]autorelease];  
  57.         }  
  58.         cell.textLabel.text=[blockedSelf.dataSource objectAtIndex:indexPath.row];  
  59.         NSLog(@"block:cellForRowAtIndexPathBlock has been invoked.");  
  60.         return cell;  
  61.     };  
  62.     self.heightForRowAtIndexPathDelegate=^(UITableView *tableView, NSIndexPath *indexPath){  
  63.         NSLog(@"block:heightForRowAtIndexPathBlock has been invoked.");  
  64.         return 60.0f;  
  65.     };  
  66.     self.didSelectRowAtIndexPathDelegate=^(UITableView *tableView, NSIndexPath *indexPath){  
  67.         NSLog(@"block:didSelectRowAtIndexPathDelegate has been invoked.");  
  68.     };  
  69. }  

然后在ViewDidLoad中调用:

[cpp]  view plain  copy  print ?

  1. [self initBlocks];  
  2.     [self loadDataSource];  
  3.     [self.tableView reloadData];  

最后,你在实例化该controller的时候,可以指定是否使用上提和下拉

[cpp]  view plain  copy  print ?

  1. self.viewController = [[[TestViewController alloc] initWithRefreshHeaderViewEnabled:YES andLoadMoreFooterViewEnabled:YES]autorelease];  

写在最后

写完之后,我用它重构了一下快易博中,新浪微博的几个视图。也省掉了一些冗余代码,如果当初在开发的时候就使用它的话,感觉还是省了一些功夫的。

它其实也还是比较简单的封装,所以还不是很具有业务相关性,同时也可见它还有很多可继续增强的功能:

1、  封装增删改查功能

2、  封装加载、操作时动画

3、  封装网络加载的统一实现

……………….

先写到这里吧。

推荐两篇讲ios block非常不错的文章:

http://lldong.github.com/blog/2011/12/30/blocks/

http://yannickloriot.com/2011/11/working-with-blocks/

源码地址:

https://github.com/yanghua/ELTableViewController