1. 程式人生 > >IOS控制元件系列二---優雅的UITableView的MVC模式設計,支援自定義下拉重新整理/上提載入更多檢視(含swift)

IOS控制元件系列二---優雅的UITableView的MVC模式設計,支援自定義下拉重新整理/上提載入更多檢視(含swift)

demo效果如下:

本小框架設計原則依舊按照之前的慣例:

1.擴充套件性好,程式碼不冗餘(整個重新整理的頭部與底部程式碼不超過300行)。

2.邏輯清晰。

3.回撥介面清晰。

4.移植性好。


對於擴充套件性本框架擴充套件點如下:

1.框架中的BaseTableView是直接擴充套件UITableView的,所以可以使用在VC中,也可以作為其他檢視的子檢視進行使用,具體的使用demo可參看,之前的BossJob專案,各位看客可以在本部落格中找。

2.支援一個tableList中的展現不同的Cell同時支援不同的cell展現不同的高度。

3.支援上拉重新整理檢視與底部載入更多檢視的自定義。

二邏輯部分:個人認為還是比較清晰的,如果發現其中邏輯不清晰的地方,在下懇請各位大神不要吝嗇您的批評批評指點,部落格本身就是一個交流的平臺,共同學習進步才是王道。具體的設計思路如下:

1.超類直接過載UITabView 並將如下介面丟給子類去擴充套件。各介面有詳細的註釋,此處不重複了。

/**
 初始化介面,子類過載時,可初始化自身的特有的物件
 */
-(void) initAttr;

/**
 構建單元格,子類需要過載此方法,方能達到效果

 @return UITableViewCell
 */
-(BaseTabViewCell*) buildTableViewCell;


/**
 獲取單元格高度,子類需要過載

 @return 單元格高度
 */
-(CGFloat) getCellHeight;



/**
 上提載入更多底部檢視,子類需要擴充套件此類,不然後返回的底部載入更多檢視是一個預設的檢視

 @return 返回一個擴充套件了ScrollViewRefreshView的具體子類
 */
-(ScrollViewRefreshView*) buildFootView;

-(void(^)()) buildLoadMoreListener;



/**
 構建下拉重新整理頭部檢視,子類需要擴充套件此類,不然後返回的下拉重新整理檢視是一個預設的檢視

 @return 返回一個擴充套件了ScrollViewHeadView的具體子類
 */
//-(ScrollViewHeadView*) buildHeadView;

對於展示不同的cell時需要過載
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

能夠顯示不同的cell是在資料模型的基類中定義了一個列舉,各個資料模型均擴充套件這個資料基類,然後每個模型返回一個惟一的列舉。在上面的方法時,遍歷當前行的單元格獲取當前資料的列舉型別,再建立這個cell就可以達到效果。部分程式碼如下:

資料模型部分:

/**
 構建UItableView cell的型別列舉

 - QuickWordsType: 快捷訊息型別
 - QuickWordsType: 快捷訊息增加型別
 */
typedef NS_ENUM(NSInteger, CellItemType) {
    
    CellItemDefaultType = 1,
    QuickWordsType,
    QuickWordsAddType,

    
};

/**
 資料型別協議介面,通常用於同一個tableView中展示不同的item型別
 */
@protocol ItemType <NSObject>

@optional
-(CellItemType) getItemType;

@end


/**
 列表中的基類介面
 */
@interface BaseModel : NSObject<ItemType>

@property(nonatomic,weak) id<ItemType> delegate;


@property(nonatomic,copy) NSString* name;

@end

重寫如下方法顯示不同的cell:
//重寫此方法,達到一個列表中展現不同的cell
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    
    BaseModel *baseModel = self.dataList[indexPath.row];
    BaseTabViewCell *cell;
    NSString *cellIdentifier;
    
    switch ([baseModel getItemType]) {
        case ChatHeadType:
            cellIdentifier = @"ChatHeadType";
            break;
        case ChatType:
            cellIdentifier = @"ChatType";
            break;
        default:
            break;
    }
    
    cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    
    if (!cell) {
        switch ([baseModel getItemType]) {
            case ChatHeadType:
                cell = [[ChatHeadCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
                break;
            case ChatType:
                cell = [[ChatCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
                break;
            default:
                break;
        }
    }
    
    [cell bindData:baseModel];
    return cell;
}

因為不同的cell的高度可能不一致,所以還需要過載如下方法:
//重寫此方法達到不同的cell有不同的高度
-(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    
    BaseModel *baseModel = self.dataList[indexPath.row];
    CGFloat height = 0.0f;
    
    switch ([baseModel getItemType]) {
            
        case ChatHeadType:
            height = 40.0f;
            break;
            
        case ChatType:
            height = 70.0f;
            break;
            
        default:
            break;
    }

    return height;
}

上面所陳述的內容涵蓋了資料模型層 、控制器層、檢視層。各位看官請自行分辨。

三。下拉重新整理/上提載入更多檢視核心設計思路:

監聽UIScrollView的contentOffset 用以實現頭部或底部檢視的插入位置 ,

監聽UIScrollView的contentSize用來更新底部載入更多檢視的位置 ,因為內容的增長是從上往下增長的,即從0--n位置增長,不然會造成底載入更多檢視位置的錯亂。而頭部永遠資料前面的位置。所以不需要擔心位置發生錯亂。

本框架封裝了基類,並將新增的介面封裝到的BaseTableView基類中方便子類擴充套件,在本DEMO中提供了2種使用方式,一種是將檢視介面,封裝到BaseTableView,另一種是封裝到具體的UItableView中,即哪一個列表需要使用上拉/下拉功能,就使用這個介面。

四。回撥介面部分,使用block,具體見程式碼。 

先看控制器層的程式碼,詳細程式碼如下:

basetableView.h:

#import <UIKit/UIKit.h>
#import "ScrollViewRefreshView.h"
#import "ScrollViewHeadView.h"

@class BaseTabViewCell;
@class BaseRefreshHeadView;





/**
 訊息頁面,列表基類
 */
@interface BaseTableView : UITableView<UITableViewDataSource,UITableViewDelegate,CAAnimationDelegate>


@property(nonatomic,strong)UITableView* mTableView;

@property(nonatomic,strong)NSMutableArray *dataList;

@property(nonatomic,assign) BOOL bIsRefreshing;

@property(nonatomic,assign) BOOL bIsLoading;

@property(nonatomic,strong) ScrollViewRefreshView* scrollFootView;

@property(nonatomic,strong) ScrollViewHeadView* scrollHeadView;




/**
 初始化介面,子類過載時,可初始化自身的特有的物件
 */
-(void) initAttr;

/**
 構建單元格,子類需要過載此方法,方能達到效果

 @return UITableViewCell
 */
-(BaseTabViewCell*) buildTableViewCell;


/**
 獲取單元格高度,子類需要過載

 @return 單元格高度
 */
-(CGFloat) getCellHeight;



/**
 上提載入更多底部檢視,子類需要擴充套件此類,不然後返回的底部載入更多檢視是一個預設的檢視

 @return 返回一個擴充套件了ScrollViewRefreshView的具體子類
 */
-(ScrollViewRefreshView*) buildFootView;

-(void(^)()) buildLoadMoreListener;



/**
 構建下拉重新整理頭部檢視,子類需要擴充套件此類,不然後返回的下拉重新整理檢視是一個預設的檢視

 @return 返回一個擴充套件了ScrollViewHeadView的具體子類
 */
//-(ScrollViewHeadView*) buildHeadView;

@end

baseTableView.m:
#import "BaseTableView.h"
#import "Constants.h"
#import "BaseModel.h"
#import "BaseTabViewCell.h"


#import "BaseRefreshHeadView.h"

@implementation BaseTableView

-(instancetype) initWithFrame:(CGRect)frame{

    if(self = [super initWithFrame:frame]){
    
        [self initAttr];
    
    }
    return self;

}

-(void) initAttr{

    self.dataList = [NSMutableArray array];
    self.backgroundColor = [UIColor colorWithRed:235.0 / 255.0 green:238.0/255.0 blue:237.0/255.0 alpha:1.0];
    self.delegate = self;
    self.dataSource = self;
    [self reloadData];
    

    [self addSubview:[self buildFootView]];
    [[self buildFootView] loadMore:[self buildLoadMoreListener]];

}

-(BaseTabViewCell*) buildTableViewCell{

    NSLog(@"這裡必須過載");
    return [[BaseTabViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"BaseTabViewCell"];;
}

-(CGFloat) getCellHeight{

    return 0.0f;
}

#pragma mark - UITableView delegate
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    
    return [self.dataList count];

}

- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    
    BaseTabViewCell* cell = [self buildTableViewCell];
    
    [cell setFrame:CGRectMake(0, 0, SCREEN_WIDTH, [self getCellHeight])];
    
    cell.backgroundColor = [UIColor colorWithRed:235.0 / 255.0 green:238.0/255.0 blue:237.0/255.0 alpha:1.0];
    
    BaseModel* data = ((BaseModel* )self.dataList[indexPath.row]);
    
    [cell bindData:data];

    return cell;
    
}


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    
    
}


- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    return [self getCellHeight];
}

//-------------------uitableView 協議方法結束-----------------------


-(ScrollViewRefreshView*) buildFootView{

    if(!self.scrollFootView){
        self.scrollFootView = [[ScrollViewRefreshView alloc] initWithFrame:self.frame];
        [self.scrollFootView addTargetWith:self];
    }
    return self.scrollFootView;
}

-(void(^)()) buildLoadMoreListener{

    LoadMoreBlock loadMore = ^(){
        
        
        double delayTime = 3.0;
        dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, delayTime * NSEC_PER_SEC);
        dispatch_after(time, dispatch_get_main_queue(), ^{
            
            [self buildFootView].loadMoreState = LoadMoreNoMoreData;
        });
    };
    
    return loadMore;
}




@end


檢視層的程式碼:

.baseTableViewcell.h

#import <UIKit/UIKit.h>

@class BaseModel;

/**
 列表中基類單元格,所有的單元均需擴充套件此類
 */
@interface BaseTabViewCell : UITableViewCell


-(void) initItemView;

-(void) bindData:(BaseModel*_Nonnull) data;


/**
 此介面為回收資源介面,子類需要擴充套件
 */
-(void) recycRes;

@end

basetableViewcell.m
#import "BaseTabViewCell.h"
#import "BaseModel.h"

@implementation BaseTabViewCell


//留給子類類擴充套件
-(void) initItemView{

}

//留給子類類擴充套件
-(void) bindData:(BaseModel*_Nonnull) data{
    
}

//回收資源介面
-(void) recycRes{

    NSLog(@"看到此日誌資訊,說明你已經考濾的很全面了,不過還需要加油^_^");
}

@end
資料層的程式碼為:

BaseModel.h

#import <UIKit/UIKit.h>



/**
 構建UItableView cell的型別列舉

 - QuickWordsType: 快捷訊息型別
 - QuickWordsType: 快捷訊息增加型別
 */
typedef NS_ENUM(NSInteger, CellItemType) {
    
    CellItemDefaultType = 1,
    QuickWordsType,
    QuickWordsAddType,

    
};

/**
 資料型別協議介面,通常用於同一個tableView中展示不同的item型別
 */
@protocol ItemType <NSObject>

@optional
-(CellItemType) getItemType;

@end


/**
 列表中的基類介面
 */
@interface BaseModel : NSObject<ItemType>

@property(nonatomic,weak) id<ItemType> delegate;


@property(nonatomic,copy) NSString* name;

@end


baseMOdel.m

#import "BaseModel.h"

@interface BaseModel ()

@end

@implementation BaseModel


-(instancetype) init{

    if(self = [super init]){
    
        self.delegate = self;
    }
    return self;
}

//override 協議介面
-(CellItemType) getItemType{

    return CellItemDefaultType;
}

@end



 上提載入更多檢視的程式碼,名字沒取好:請自行忽略名字怪異大笑大笑大笑,對於本檢視,本demo中設定一些狀態列舉,用來控制重新整理的表現過程。

#import <UIKit/UIKit.h>


typedef NS_ENUM(NSInteger,LoadMore){
    
    LoadMoreLoading = 1,
    LoadMoreComplete,
    LoadMoreNoMoreData,
    
};





typedef void(^LoadMoreBlock)(void);

/**
 滾動列表中下拉重新整理/上提載入更多的公共重新整理檢視
 */
@interface ScrollViewRefreshView : UIView


@property(nonatomic,assign) LoadMore loadMoreState;



/**
 初始View佈局與全域性屬性
 */
-(void) initView;

/**
 將當前檢視繫結到滾動列表中

 @param scrollView 目標滾動列表
 */
-(void) addTargetWith:(UIScrollView* ) scrollView;



/**
 *  上提載入更多回調介面
 *  @param block 載入更多 block
 */
- (void)loadMore:(void(^)())block;


/**
 載入更多完成介面
 */
-(void) loadMoreComplete;



@end

.m檔案:
#import "ScrollViewRefreshView.h"
#import "Constants.h"



#define kRefreshViewWidth  200
#define kRefreshViewHeight 80

#define kMaxPullUpDistance   84
#define MarginForLoadMore  60       //用來控制,上提多少point才呼叫載入更多介面


@interface ScrollViewRefreshView ()

@property (nonatomic, strong) UIScrollView *scrollView;

@property (nonatomic, assign) CGSize contentSize;
@property (nonatomic, copy) LoadMoreBlock loadMoreBlock;


@property(nonatomic,strong) UILabel* labelRefresh;

@end


@implementation ScrollViewRefreshView

-(instancetype) initWithFrame:(CGRect)frame{
    
    self = [super initWithFrame:CGRectMake(0,CGRectGetHeight(frame), SCREEN_WIDTH, kRefreshViewHeight)];
    
    if(self){
        
    }
    return self;

}

-(void) initView{
    
    
    self.labelRefresh = [[UILabel alloc] initWithFrame:CGRectMake(20, 0, self.frame.size.width, 40)];
    self.labelRefresh.text = @"上提載入更多";
    self.labelRefresh.backgroundColor = [UIColor greenColor];
    [self insertSubview:self.labelRefresh atIndex:0];

}

-(void) addTargetWith:(UIScrollView* ) scrollView {
    
    self.scrollView = scrollView;
    [self.scrollView insertSubview:self atIndex:0];
    [self.scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
    [self.scrollView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
    self.hidden = NO;
    

    self.labelRefresh = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, 40)];
    self.labelRefresh.text = @"上提載入更多";
    self.labelRefresh.textColor = [UIColor greenColor];
    self.labelRefresh.textAlignment = NSTextAlignmentCenter;
    self.labelRefresh.font = [UIFont systemFontOfSize:12.0];
    [self insertSubview:self.labelRefresh atIndex:0];
    
    
    //延遲 0.2s更新位置,防止位置被遮擋
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 0.2 * NSEC_PER_SEC);
    dispatch_after(time, dispatch_get_main_queue(), ^{
        self.center = CGPointMake(self.center.x, self.contentSize.height + kRefreshViewHeight/2);
    });
}


- (void)loadMore:(void (^)())block{
    self.loadMoreBlock = block;
}

-(void)loadMoreComplete{

    self.loadMoreState  = LoadMoreComplete;

    self.center = CGPointMake(self.center.x, self.contentSize.height + kRefreshViewHeight/2);
}

//override method
-(void) setLoadMoreState:(LoadMore)loadMoreState{
    
    if(_loadMoreState != loadMoreState){
    
         _loadMoreState = loadMoreState;
    }

    switch(_loadMoreState){
    
        case LoadMoreLoading:{
            self.labelRefresh.text = @"正在載入,請稍後...";
            
        }break;
            
        case LoadMoreComplete:{
        
            self.labelRefresh.text = @"上提載入更多";
            
        }break;
        case LoadMoreNoMoreData:{
            
            self.labelRefresh.text = @"---HAPPY END----";
        
        }break;
    
    }

}


#pragma mark - KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    
    if ([keyPath isEqualToString:@"contentSize"]) {
        self.contentSize = [[change valueForKey:NSKeyValueChangeNewKey] CGSizeValue];
        if (self.contentSize.height >= CGRectGetHeight(self.scrollView.frame)) {
            self.hidden = NO;
        }
        self.center = CGPointMake(self.center.x, self.contentSize.height + kRefreshViewHeight/2); //防止下拉重新整理時,位置顯示不對的情況
    }
    
    if ([keyPath isEqualToString:@"contentOffset"]) {
        
        if(self.loadMoreState == LoadMoreLoading || self.loadMoreState == LoadMoreNoMoreData) return;
        
        CGPoint contentOffset = [[change valueForKey:NSKeyValueChangeNewKey] CGPointValue];
        
        if (contentOffset.y >= MarginForLoadMore) {
            
            if (!self.scrollView.tracking ) {
                self.hidden = NO;
                self.center = CGPointMake(self.center.x, self.contentSize.height + kRefreshViewHeight/2);
                self.loadMoreState = LoadMoreLoading;
                self.scrollView.contentInset = UIEdgeInsetsMake(0, 0, self.labelRefresh.frame.size.height, 0);
                if (self.loadMoreBlock) {
                    self.loadMoreBlock();
                }

            }
            
        }
    }
}

#pragma mark - dealloc
- (void)dealloc {
    [self.scrollView removeObserver:self forKeyPath:@"contentOffset"];
    [self.scrollView removeObserver:self forKeyPath:@"contentSize"];
}

@end












 下拉重新整理檢視:
#import <UIKit/UIKit.h>


typedef NS_ENUM(NSInteger,PullDownRefresh){
    
    PullDownRefreshNomral = 1,
    PullDownRefreshing,
    PullDownRefreshComplete,
    
};


typedef void(^PullDownRefreshBlock)(void);

/**
 ScrollView列表的下拉重新整理檢視
 */
@interface ScrollViewHeadView : UIView


@property (nonatomic, strong) UIScrollView *scrollView;


//@property (nonatomic, assign) CGFloat originOffset;
@property (nonatomic, assign) CGFloat progress;

@property (nonatomic, assign) BOOL isLoading;
@property (nonatomic, assign) BOOL notTracking;

@property(nonatomic,strong) UILabel* labelRefresh;

@property (nonatomic, copy) PullDownRefreshBlock refreshingBlock;

@property(nonatomic,assign) PullDownRefresh refershState;


/**
 將當前檢視繫結到滾動列表中
 
 @param scrollView 目標滾動列表
 */
-(void) addTargetWith:(UIScrollView* ) scrollView;



/**
 *  上提載入更多回調介面
 *  @param bolck 下拉重新整理 block
 */
- (void) refresh:(PullDownRefreshBlock) bolck;


/**
 載入更多完成介面
 */
-(void) refreshComplete;



@end


.m檔案:

#import "ScrollViewHeadView.h"
#import "Constants.h"


#define kRefreshViewWidth  200
#define kRefreshViewHeight 80

#define kMaxPullDownDistance   84

#define MarginForRefrshing  60       //用來控制,上提多少point才呼叫載入更多介面

@implementation ScrollViewHeadView

-(instancetype) initWithFrame:(CGRect)frame{
    
    self = [super initWithFrame:CGRectMake(0,-kRefreshViewHeight,SCREEN_WIDTH, kRefreshViewHeight)];
    if(self){
        
    }
    return self;
    
}


-(void) addTargetWith:(UIScrollView* ) scrollView {
    
    
    // self.originOffset = 70.0;
    
    self.scrollView = scrollView;
    [self.scrollView insertSubview:self atIndex:0];
    [self.scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
   
    //self.backgroundColor = [UIColor yellowColor];
    
    self.labelRefresh = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, kRefreshViewHeight)];
    self.labelRefresh.text = @"下拉重新整理資料";
    self.labelRefresh.textColor = [UIColor greenColor];
    self.labelRefresh.textAlignment = NSTextAlignmentCenter;
    self.labelRefresh.font = [UIFont systemFontOfSize:12.0];
    [self insertSubview:self.labelRefresh atIndex:0];
    
    
//    //延遲 0.2s更新位置,防止位置被遮擋
//    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 0.2 * NSEC_PER_SEC);
//    dispatch_after(time, dispatch_get_main_queue(), ^{
//        self.center = CGPointMake(self.center.x, self.contentSize.height + kRefreshViewHeight/2);
//    });
}

//override --method
-(void) setRefershState:(PullDownRefresh)refershState{
    
    if(_refershState != refershState){
    
        _refershState = refershState;
    }
    
    switch(_refershState){
            
        case PullDownRefreshNomral:{
            
            self.labelRefresh.text = @"下拉重新整理資料";
        
        }break;
    
        case PullDownRefreshing:{
            
            self.labelRefresh.text = @"正在重新整理,請稍後...";
        
        }break;
            
            
        case PullDownRefreshComplete:{
            
            self.labelRefresh.text = @"重新整理完成";
        
        }break;
    
    }

}

-(void) refresh:(PullDownRefreshBlock)bolck{

    self.refreshingBlock = bolck;
}

-(void) refreshComplete{

    self.refershState = PullDownRefreshNomral;
    self.scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
}


#pragma mark - KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqualToString:@"contentOffset"]) {
        
        if(self.refershState == PullDownRefreshing) return;
        
        CGPoint contentOffset = [[change valueForKey:NSKeyValueChangeNewKey] CGPointValue];
        if (MarginForRefrshing + contentOffset.y <= 0) {
            
            if (!self.scrollView.tracking) {
                
                self.refershState = PullDownRefreshing;
                self.scrollView.contentInset = UIEdgeInsetsMake(kMaxPullDownDistance, 0, 0, 0);
                
                if (self.refreshingBlock) {
                    self.refreshingBlock();
                }
            }

        }
    }
}

#pragma mark - dealloc
- (void)dealloc {
    [self.scrollView removeObserver:self forKeyPath:@"contentOffset"];
}

@end


swift版本為oc的翻譯版本,。。。。。

tab;eView相關的檔案:

import Foundation
import UIKit

class BaseTableView : UITableView,UITableViewDelegate,UITableViewDataSource{
    
    
    var dataList:Array<BaseModel>?
    
    
    override init(frame: CGRect, style: UITableViewStyle) {
        super.init(frame: frame, style: style)
        self.frame = frame
        initAttr()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func initAttr(){
        
        self.dataList = Array()
        self.dataSource = self
        self.delegate = self
        
    }

    
    
    /// 獲取單元格高度--子類需要過載
    ///
    /// - Returns: 單元格高度
    func getCellHeight() -> CGFloat {
        
        return 70.0
    }
    
    
    /// 構建單元格檢視子類需過載,不然顯示預設檢視
    ///
    /// - Returns: 單元格檢視
    func buildTableViewCell() -> BaseTabViewCell {
        print("子類趕緊支過載吧,不然單元格是預設檢視")
        
       return BaseTabViewCell.init(style: UITableViewCellStyle.default, reuseIdentifier: "BaseCell")
        
    }
    
    //implements protocol
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        return (dataList?.count)!
    }
    
    
    //implements protocol
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        
        return getCellHeight()
    }
    
    //implements protocol
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = buildTableViewCell()
        
        cell.frame = CGRect(x : 0, y : 0, width : self.frame.width, height : self.getCellHeight())
        
        
        let data :BaseModel = (dataList?[indexPath.row])!
        
        cell.bindData(data: data)
        
        return cell
    
    }
    
    
}

import Foundation
import UIKit

class QuickWordsView: BaseTableView {
    
    
    
//    init(style: UITableViewStyle, reuseIdentifier: String){
//        //super.init(reuseIdentifier:reuseIdentifier,style:style)
//        
//    }
//    
//    required init?(coder aDecoder: NSCoder) {
//        fatalError("init(coder:) has not been implemented")
//    }
    
    override func initAttr() {
        
        super.initAttr()
        
        
        self.backgroundColor = UIColor.init(red: 235.0 / 255.0, green:238.0/255.0, blue:237.0/255.0, alpha:1.0)
        
        let wrods:[String] = ["能同時開發android/ios","我可以把簡歷發您看看麼?",
                               "我能去貴司面試麼?","對不起,貴司提供的職位可能不太適合,謝謝"]
        
        for item in wrods{
        
            let model : QuickWordsModel = QuickWordsModel()
            
            model.name = item
            
            self.dataList?.append(model)
        }
        
        
        //新增底部載入更多檢視
        let scrollFootView : ScrollViewRefreshView = ScrollViewRefreshView.init(frame: self.frame)
        scrollFootView.addTargetWith(scrollView: self)
        
        scrollFootView.loadMore = {() ->Void in
        
            if((self.dataList?.count)! > 10){
            
                scrollFootView.setLoadMoreState(loadMoreState: LoadMoreType.LoadMoreNoMoreData)
                return
            }
            
            
            DispatchQueue.main.asyncAfter(deadline:DispatchTime.now() + Double(Int64(3 *  Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)){
                
                for i in 0 ..< 3{
                
                    let model:QuickWordsModel = QuickWordsModel()
                    
                    model.name = String.init(format: "loadmore data %d", i)
                    
                    self.dataList?.append(model)
                }
                
                self.reloadData()
                
                scrollFootView.loadMoreComplete()
                
            }
        }
        
        //新增頭部重新整理檢視
        let scrollHeadView : ScrollViewHeadView = ScrollViewHeadView.init(frame: self.frame)
        scrollHeadView.addTargetWith(scrollView: self)
        scrollHeadView.refrsh(block:{()->Void in
        
            DispatchQueue.main.asyncAfter(deadline:DispatchTime.now() + Double(Int64(3 *  Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)){
                
                self.dataList?.removeAll()
                
                for i in 0 ..< 1{
                    
                    let model:QuickWordsModel = QuickWordsModel()
                    
                    model.name = String.init(format: "refresh data %d", i)
                    
                    self.dataList?.append(model)
                }
                
                self.reloadData()
                
                scrollHeadView.refreshComplete()
                
            }
            
        })
        
    }
    
    
    override func buildTableViewCell() -> BaseTabViewCell {
        return QiuckWordsCell(style:UITableViewCellStyle.default, reuseIdentifier : "QiuckWordsCell")
    }
    
    override func getCellHeight() -> CGFloat {
        
        return 40.0
    }
}
tableViewCell相關檔案 ,先上基類檔案:
import Foundation
import UIKit


/// UItableView 中的單元格基類  所有的單元格均需要過載本灰
class BaseTabViewCell: UITableViewCell {
    
    
    var label:UILabel?
    
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        initItemView()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    /// 子類需要過載本介面
    func initItemView() {
        
        label = UILabel(frame:CGRect(x : 10.0 , y : self.center.y, width : 80, height:30))
        
        label?.text = "韋小寶"
        
        label?.font = UIFont.systemFont(ofSize: 14)
        
        self.addSubview(label!)
        
    }
    
    
    
    /// 將資料模型與單元格繫結起來,子類需要過載本介面
    ///
    /// - Parameter data: <#data description#>
    func bindData(data:BaseModel){
        
    }
    
    
    /// 回收資源介面,子類必要時過載
    func recycRes(){

    }
    
}



具體的單元格:

import Foundation
import UIKit

class QiuckWordsCell: BaseTabViewCell {
    
    var labelWords:UILabel?
    
    var data:QuickWordsModel?
    
    
    override func initItemView() {
    
        self.backgroundColor = UIColor.clear
        
        labelWords = UILabel(frame:CGRect(x : 10.0 , y : 0, width : self.frame.width, height:30))
        labelWords?.textAlignment = NSTextAlignment.center
        labelWords?.text = "韋小寶"
        
        labelWords?.font = UIFont.systemFont(ofSize: 14)
        
        self.addSubview(labelWords!)
    }
    
    override func bindData(data: BaseModel) {
        
        if self.data != data {
            
            self.data = data as? QuickWordsModel
            labelWords?.text = self.data?.name
        }
    }
}


資料框架類:

import Foundation


enum CellItemType {
    
    case DefaultType
    
    case QuickWordsType
    
    case QuickWordsAddType
    
}


/// 單元格對應的資料型別介面
protocol ItemType {
    
    func getItemType() -> CellItemType
}


class BaseModel : NSObject,ItemType{

    var name:String = "defalue name"
    
    var delegate: ItemType?
    
    override init() {
        super.init()
        
        self.delegate = self
    }
    
    //implement ItemType interface
    func getItemType() -> CellItemType {
        
        return CellItemType.DefaultType
    }
    

}


具體的資料模型:

import Foundation


class QuickWordsModel: BaseModel {
    
    
    override func getItemType() -> CellItemType {
        
        return CellItemType.QuickWordsType
    }
}


在viewController中使用使用

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        test()
    
    }
    
    func test() {
        
        let qwv: QuickWordsView = QuickWordsView.init(frame:CGRect(x : 0, y : 50,width: self.view.frame.width, height: 400))
        qwv.initAttr()
        qwv.separatorInset = UIEdgeInsetsMake(0, 10, 0, 10)
        qwv.tableFooterView = UIView(frame:CGRect.zero)
        
        self.view.addSubview(qwv)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}


相關推薦

IOS控制元件系列---優雅的UITableView的MVC模式設計,支援定義重新整理/載入檢視(swift)

demo效果如下: 本小框架設計原則依舊按照之前的慣例: 1.擴充套件性好,程式碼不冗餘(整個重新整理的頭部與底部程式碼不超過300行)。 2.邏輯清晰。 3.回撥介面清晰。 4.移植性好。 對於擴充套件性本框架擴充套件點如下: 1.框架中的

IOS控制元件系列----使用UITableView實現網格佈局,定義顯示列數

先放一引效果圖: 在IOS中達到類似Android中的GridLayout 通常是使用UIConlectionView,這個元件是平果公司已經封裝好的,直接實現相應的介面即可。不知道各位道友是否也曾想過用UItableView來擼一個這個東西,這可能會有一點偏執,但對

定義重新整理載入控制元件(SwipeRefreshLayout + recyclerView)

感覺還可以的star下謝謝! 效果圖:(效果圖迴圈播放後,總感覺有些詭異!, 可能是gif截的點不對, 在手機上看效果正常的) 我就不講程式碼是如何實現的了。說下實現了什麼內容: 支援自動下拉重新整理 //設定自動下拉重新整理,切記要

iOS開發-ios7重新整理 載入快速整合

                在ios7之前,一直在使用開源的EGO庫。但是,在使用過程中發現,普遍封裝得過於複雜、耦合性強,不利於整合到自己的專案中。另外,在ios7之後,一些原有的下拉重新整理,上提載入控制元件表現的就不是那麼出色了。除了可能出錯外,也不符合扁平化的風格。後來,在code4App上發現了

定義重新整理控制元件-仿美團重新整理效果

概述   下拉重新整理是平時專案中最常用的功能,今天要說的就是如何自定義下拉重新整理控制元件。   第三方重新整理控制元件也比較多,例如Android-PullToRefresh,XListView等,但是這些控制元件自定義重新整理頭部不那麼容易擴充套件,它

android定義重新整理載入控制元件

import android.content.Context; import android.graphics.Point; import android.support.v4.view.MotionEventCompat; import android.support.v4.view.NestedScro

優雅地為RecyclerView加上頭部、重新整理、自動載入

一、概述 我們在寫專案的時候,永遠都離不開ListView、RecyclerView這類的控制元件,幾乎是在任何的APP中都可以看到他們的影子,但是RecyclerView並沒有像ListView提供了addHeadView、addFooterView

畢業設計之android混合模式開發第一天--具有重新整理和頁面載入等待的WebView搭建

第一次真正接觸android的混合模式開發,之前瞭解過如何進行混合模式的開發,常見的是通過WebView元件載入url,使用HTML5和CSS3構建手機端響應式佈局。 今天主要是搭建出一個可載入url,具有下拉重新整理和頁面等待的WebView。 2.頁面等待的實現主要是

C#:C#控制元件系列 (文字框類控制元件

文字框類控制元件1. Label 控制元件1.1. 常用屬性:1.1.1. Text屬性:用來設定或返回標籤控制元件中顯示的文字資訊。1.1.2. AutoSize屬性:用來獲取或設定一個值,該值指示是否自動調整控制元件的大小以完整顯示其內容。——  取值為true時,控制元

Android定義控制元件系列定義開關按鈕(一)

這一次我們將會實現一個完整純粹的自定義控制元件,而不是像之前的組合控制元件一樣,拿系統的控制元件來實現;計劃分為三部分:自定義控制元件的基本部分,和自定義控制元件的自定義屬性; 下面就開始第一部分的編寫,本次以一個定義的開關按鈕為例,下面就開始吧: 先看看效果,一個點選開

Android定義控制元件系列()—icon+文字的多種效果實現

今天給大家帶來一個很簡單但是很常用的控制元件ButtonExtendM,在開發中我們經常會用到圖片加文字的組合控制元件,像這樣: 以上圖片都是從微信上擷取的。(暫時沒有找到icon在下,文字在上的例子) 下面我們通過一個控制元件來實現上下左右全部

Android定義控制元件實戰——實現仿IOS重新整理載入 PullToRefreshLayout

         下拉重新整理控制元件,網上有很多版本,有自定義Layout佈局的,也有封裝控制元件的,各種實現方式的都有。但是很少有人告訴你具體如何實現的,今天我們就來一步步實現自己封裝的 PullToRefreshLayout 完美的解決下拉重新整理,上拉載入問題。  

自個兒寫Android的重新整理/載入控制元件

前段時間自己寫了一個能夠“通用”的,支援下拉重新整理和上拉載入的自定義控制元件。可能現如今這已經不新鮮了,但有興趣的朋友還是可以一起來看看的。 與通常的View配合使用(比如ImageView) 與ListView配合使用 與Recycl

RecyclerView實現重新整理與自動載入控制元件封裝

CommonAdapter.java public abstract class CommonAdapter<T> extends RecyclerView.Adapter<ViewHolder> { protected Context mContext; prot

Android定義控制元件並且使其可以在xml中定義屬性

package org.xiaom.customView.view; import org.xiaom.customView.R; public class MyView extends LinearLayout { private View root = null; // 上面的img priva

JAVA應用程式整合控制元件JxBrowser v7.2來啦!允許定義錯誤頁面

JxBrowser更新至最新版v7.2,允許針對HTTP和網路錯誤覆蓋標準Chromium錯誤頁面,允許設定不安全的來源視為安全,改進多種功能,修復多項Bug,具體更新情況如下: 新增功能 自定義錯誤頁面:通過兩個新的回撥擴充套件了API,該回調允許針對HTTP和網路錯誤覆蓋標準Chrom

使用 CSS overscroll-behavior 控制滾動行為:定義刷新和溢出效果

pull str 新的 title contain 下拉刷新 介紹 select data CSS 的新屬性 overscroll-behavior 允許開發者覆蓋默認的瀏覽器滾動行為,一般用在滾動到頂部或者底部。 背景 滾動邊界和滾動鏈接(boundary & c

Android打造 ListView GridView等 通用的重新整理 自動載入元件

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

vue定義元件

<template> <div class="selection-component"> <div class="selection-show" @click="toggleDrop"> <span :class="{'active': isDr

IOS-給UIScrollView(包括繼承它的UITableView、UICollectionView)新增重新整理-載入

IOS裡面用到的下拉重新整理、上拉載入更多控制元件,開源的第三方框架很多,我們可以直接拿過來用,別人造好的輪子我們就沒有必要再造一遍了,這裡推薦幾款下拉重新整理、上拉載入更多控制元件 只有下拉重新整