你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS學習筆記13— UITableView封裝之下拉-上提-圖片異步加載

iOS學習筆記13— UITableView封裝之下拉-上提-圖片異步加載

編輯:IOS開發綜合

 


做過移動端開發的人都知道,列表控件是最常用的控件之一。iOS裡的列表控件是UITableView,其實Apple的開發人員對於UITableView的設計已經夠好的了(簡單易用,擴展性非常強等等)。

但對於展示邏輯單一的移動端系統軟件,你還是能感覺到有些繁瑣(或許是程序員天生就有些懶惰的毛病吧)。

來看看它到底繁瑣在哪兒了。首先,它的使用頻率太高了;第二,它通常不是只呈現一下數據就完事了,一般都會跟隨下拉刷新、上提加載更多功能,當然通常還要跟網絡下載數據、圖片打交道;第三,MVC模式是ios開發的慣用模式,隨之而來的是一大堆協議的實現(無論你是再寫一次也好,拷貝也罷,反正做這些工作都讓人覺得索然無味)。

沖著這些,今天就把UITableView常見的使用模式封裝了一下。具體做了以下幾件事:

1、  內嵌了下拉刷新(EGORefreshTableHeaderView)、上提加載更多(LoadMoreTableFooterView)

2、  內置實現了UITableViewDataSource、UITableViewDelegate這兩個通常必須實現的協議,對於自實現的邏輯以Block的形式對客戶代碼開放

3、  內置實現了1中提到的兩個組件的回調協議,同上,自實現的邏輯以Block的形式對外開放

4、  內置實現了EGORefreshTableHeaderView、LoadMoreTableFooterView與UITableView交互必須實現的UIScrollViewDelegate協議

5、  內置實現了異步圖片下載(可選)

 

你可以到我的Github上,查看源碼。稱它為ELTableViewController是取了EGORefreshTableHeaderView以及LoadMoreTableFooterView的首字母。

這份代碼中包含了一個示例程序以及三個必備組件:

1、  EGORefreshTableHeaderView

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

3、  Apple官方提供的異步下載UITableView中的圖片的示例組件(IconDownLoader),這個只適用於下載類似於社交網絡中的用戶頭像,不建議使用它來下載那些大圖片,因為它甚至都沒有緩存(如果圖片很大,推薦使用SDImage)

 

代碼解讀
它已經內置實現了這些協議,所以在你使用它的時候,無需設置和實現。

 

[cpp]
@interface ELTableViewController : UIViewController 

UITableViewDelegate, 
UITableViewDataSource, 
EGORefreshTableHeaderDelegate, 
LoadMoreTableFooterDelegate, 
IconDownloaderDelegate 

 

對於不斷變化的業務邏輯,這裡提供了所有需要實現的block:

[cpp]
 //blocks for UITableView delegate 
typedef UITableViewCell* (^cellForRowAtIndexPathDelegate) (UITableView *,NSIndexPath *); 
typedef CGFloat (^heightForRowAtIndexPathDelegate) (UITableView *,NSIndexPath *); 
typedef void (^didSelectRowAtIndexPathDelegate) (UITableView *,NSIndexPath *); 
 
//blocks for refresh and load more 
typedef void (^refreshDataSourceFunc) (void); 
typedef void (^loadMoreDataSourceFunc) (void); 
 
typedef void (^refreshDataSourceCompleted) (void); 
typedef void (^loadMoreDataSourceCompleted) (void); 
//use to load image (async) 
typedef void (^loadImagesForVisiableRowsFunc) (void); 
typedef void (^appImageDownloadCompleted) (NSIndexPath *); 

 


它們以屬性的形式對外公開:

[cpp]
//property for blocks 
@property (nonatomic,copy) cellForRowAtIndexPathDelegate cellForRowAtIndexPathDelegate; 
@property (nonatomic,copy) heightForRowAtIndexPathDelegate heightForRowAtIndexPathDelegate; 
@property (nonatomic,copy) didSelectRowAtIndexPathDelegate didSelectRowAtIndexPathDelegate; 
 
@property (nonatomic,copy) loadMoreDataSourceFunc loadMoreDataSourceFunc; 
@property (nonatomic,copy) refreshDataSourceFunc refreshDataSourceFunc; 
@property (nonatomic,copy) refreshDataSourceCompleted refreshDataSourceCompleted; 
@property (nonatomic,copy) loadMoreDataSourceCompleted loadMoreDataSourceCompleted; 
 
@property (nonatomic,copy) loadImagesForVisiableRowsFunc loadImagesForVisiableRowsFunc; 
@property (nonatomic,copy) appImageDownloadCompleted appImageDownloadCompleted; 

對於上提加載更多、下拉刷新、圖片異步加載這幾個功能都是可選的,它們以組件的形式存在。比如,在實例化該controller的時候你就可以設置上提和下拉是否可用。而對於圖片下載,你只要不實現其相應得block,它也不會對你造成額外的負擔。


[cpp]
- (id)initWithRefreshHeaderViewEnabled:(BOOL)enableRefreshHeaderView  
andLoadMoreFooterViewEnabled:(BOOL)enableLoadMoreFooterView; 
 
- (id)initWithRefreshHeaderViewEnabled:(BOOL)enableRefreshHeaderView  
          andLoadMoreFooterViewEnabled:(BOOL)enableLoadMoreFooterView  
                     andTableViewFrame:(CGRect)frame; 

[cpp]
#pragma mark - UITableView Delegate - 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ 
    if (nil==self.dataSource) { 
        return 0; 
    } 
     
    return [self.dataSource count]; 

 
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ 
    if (!self.cellForRowAtIndexPathDelegate) { 
        @throw [NSException exceptionWithName:@"Framework Error"  
                                       reason:@"Must be setting cellForRowAtIndexPathBlock for UITableView" userInfo:nil]; 
    } 
    return self.cellForRowAtIndexPathDelegate(tableView,indexPath); 

 
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ 
    if (!self.heightForRowAtIndexPathDelegate) { 
        @throw [NSException exceptionWithName:@"Framework Error"  
                                       reason:@"Must be setting heightForRowAtIndexPathDelegate for UITableView" userInfo:nil]; 
    } 
    return self.heightForRowAtIndexPathDelegate(tableView,indexPath); 

 
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ 
    if (self.didSelectRowAtIndexPathDelegate) { 
        self.didSelectRowAtIndexPathDelegate(tableView,indexPath); 
    } 

 
#pragma mark - LoadMoreTableFooterDelegate Methods - 
- (void)loadMoreTableFooterDidTriggerRefresh:(LoadMoreTableFooterView *)view{ 
    if (self.loadMoreDataSourceFunc&&self.loadMoreDataSourceCompleted) { 
        self.loadMoreDataSourceFunc(); 
         
        double delayInSeconds = 3.0; 
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); 
        dispatch_after(popTime, dispatch_get_main_queue(),  
                       self.loadMoreDataSourceCompleted); 
    } 

 
- (BOOL)loadMoreTableFooterDataSourceIsLoading:(LoadMoreTableFooterView *)view{ 
    return self.isLoadingMore; 

 
#pragma mark - EGORefreshTableHeaderDelegate Methods - 
-(void)egoRefreshTableHeaderDidTriggerRefresh:(EGORefreshTableHeaderView *)view{ 
    if (self.refreshDataSourceFunc&&self.refreshDataSourceCompleted){ 
        self.refreshDataSourceFunc(); 
         
        double delayInSeconds = 3.0; 
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); 
        dispatch_after(popTime, dispatch_get_main_queue(),  
                       self.refreshDataSourceCompleted); 
    } 

 
-(BOOL)egoRefreshTableHeaderDataSourceIsLoading:(EGORefreshTableHeaderView *)view{ 
    return self.isRefreshing; 

 
-(NSDate *)egoRefreshTableHeaderDataSourceLastUpdated:(EGORefreshTableHeaderView *)view{ 
    return [NSDate date]; 

 
#pragma mark - UIScrollViewDelegate Methods - 
-(void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{ 
    self.currentOffsetPoint=scrollView.contentOffset; 

 
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{ 
    CGPoint pt=scrollView.contentOffset; 
    if (self.currentOffsetPoint.y<pt.y) { 
        [self.loadMoreFooterView loadMoreScrollViewDidScroll:scrollView]; 
    }else {      
        [self.refreshHeaderView egoRefreshScrollViewDidScroll:scrollView]; 
    } 

 
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ 
    CGPoint pt=scrollView.contentOffset; 
    if (self.currentOffsetPoint.y<pt.y) { 
        [self.loadMoreFooterView loadMoreScrollViewDidEndDragging:scrollView]; 
    }else { 
        [self.refreshHeaderView egoRefreshScrollViewDidEndDragging:scrollView]; 
    } 
     
    if (!decelerate&&self.loadImagesForVisiableRowsFunc) { 
        self.loadImagesForVisiableRowsFunc(); 
    } 

 
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{ 
    if (self.loadImagesForVisiableRowsFunc) { 
        self.loadImagesForVisiableRowsFunc(); 
    } 

 
#pragma mark - download image async - 
-(void)appImageDidLoad:(NSIndexPath *)indexPath{ 
    if (self.appImageDownloadCompleted) { 
        self.appImageDownloadCompleted(indexPath); 
    } 


ELTableViewController 的使用

創建一個新的controller繼承自:ELTableViewController;

override父類的initBlocks方法:


[cpp]
#pragma mark - private methods - 
- (void)loadDataSource{ 
    self.dataSource=[NSMutableArray array]; 
    [self.dataSource addObject:@"dataSource_1"]; 
    [self.dataSource addObject:@"dataSource_2"]; 
    [self.dataSource addObject:@"dataSource_3"]; 
    [self.dataSource addObject:@"dataSource_4"]; 
    [self.dataSource addObject:@"dataSource_5"]; 
    [self.dataSource addObject:@"dataSource_6"]; 
    [self.dataSource addObject:@"dataSource_7"]; 
    [self.dataSource addObject:@"dataSource_8"]; 
    [self.dataSource addObject:@"dataSource_9"]; 
    [self.dataSource addObject:@"dataSource_10"]; 

 
- (void)initBlocks{ 
    __block TestViewController *blockedSelf=self; 
     
    //load more 
    self.loadMoreDataSourceFunc=^{ 
        [blockedSelf.dataSource addObject:@"loadMoreDataSourceBlock_1"]; 
        [blockedSelf.dataSource addObject:@"loadMoreDataSourceBlock_2"]; 
        [blockedSelf.dataSource addObject:@"loadMoreDataSourceBlock_3"]; 
        [blockedSelf.dataSource addObject:@"loadMoreDataSourceBlock_4"]; 
        [blockedSelf.dataSource addObject:@"loadMoreDataSourceBlock_5"]; 
         
        blockedSelf.isLoadingMore=YES; 
        [self.tableView reloadData]; 
         
        NSLog(@"loadMoreDataSourceBlock was invoked"); 
    }; 
     
    //load more completed 
    self.loadMoreDataSourceCompleted=^{ 
        blockedSelf.isLoadingMore=NO; 
        [blockedSelf.loadMoreFooterView loadMoreScrollViewDataSourceDidFinishedLoading:self.tableView]; 
         
        NSLog(@"after loadMore completed"); 
    }; 
     
    //refresh 
    self.refreshDataSourceFunc=^{ 
        blockedSelf.dataSource=[NSMutableArray array]; 
        [blockedSelf.dataSource addObject:@"refreshDataSourceBlock_1"]; 
        [blockedSelf.dataSource addObject:@"refreshDataSourceBlock_2"]; 
        [blockedSelf.dataSource addObject:@"refreshDataSourceBlock_3"]; 
        [blockedSelf.dataSource addObject:@"refreshDataSourceBlock_4"]; 
        [blockedSelf.dataSource addObject:@"refreshDataSourceBlock_5"]; 
         
        blockedSelf.isRefreshing=YES; 
        [self.tableView reloadData]; 
         
        NSLog(@"refreshDataSourceBlock was invoked"); 
    }; 
     
    //refresh completed 
    self.refreshDataSourceCompleted=^{ 
        blockedSelf.isRefreshing=NO; 
        [blockedSelf.loadMoreFooterView loadMoreScrollViewDataSourceDidFinishedLoading:self.tableView]; 
         
        NSLog(@"after refresh completed"); 
    }; 
     
    self.cellForRowAtIndexPathDelegate=^(UITableView *tableView, NSIndexPath *indexPath){ 
        static NSString *cellIdentifier=@"cellIdentifier"; 
        UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier]; 
        if (!cell) { 
            cell=[[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]autorelease]; 
        } 
         
        cell.textLabel.text=[blockedSelf.dataSource objectAtIndex:indexPath.row]; 
         
        NSLog(@"block:cellForRowAtIndexPathBlock has been invoked."); 
         
        return cell; 
    }; 
     
    self.heightForRowAtIndexPathDelegate=^(UITableView *tableView, NSIndexPath *indexPath){ 
        NSLog(@"block:heightForRowAtIndexPathBlock has been invoked."); 
        return 60.0f; 
    }; 
     
    self.didSelectRowAtIndexPathDelegate=^(UITableView *tableView, NSIndexPath *indexPath){ 
        NSLog(@"block:didSelectRowAtIndexPathDelegate has been invoked."); 
    }; 
     

 

 


 

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved