IOS控件系列二---优雅的UITableView的MVC模式设计,支持自定义下拉刷新/上提加载更多视图(含swift)
来源:互联网 发布:java访问https 编辑:程序博客网 时间:2024/06/05 06:46
demo效果如下:
本小框架设计原则依旧按照之前的惯例:
1.扩展性好,代码不冗余(整个刷新的头部与底部代码不超过300行)。
2.逻辑清晰。
3.回调接口清晰。
4.移植性好。
对于扩展性本框架扩展点如下:
1.框架中的BaseTableView是直接扩展UITableView的,所以可以使用在VC中,也可以作为其他视图的子视图进行使用,具体的使用demo可参看,之前的BossJob项目,各位看客可以在本博客中找。
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
demo源码:github 劳驾留下您的星星与评论,谢谢
swift版本为oc的翻译版本,。。。。。
tab;eView相关的文件:
import Foundationimport UIKitclass 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 Foundationimport UIKitclass 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 }}
import Foundationimport 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 Foundationimport UIKitclass 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 Foundationenum 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 Foundationclass QuickWordsModel: BaseModel { override func getItemType() -> CellItemType { return CellItemType.QuickWordsType }}
在viewController中使用使用
import UIKitclass 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)
- Android自定义控件(二) 下拉刷新,上拉分页加载更多(支持ListView, GridView, ScrollView)
- 支持任意View下拉刷新/下拉加载更多的控件
- IOS-给UIScrollView(包括继承它的UITableView、UICollectionView)添加下拉刷新-上拉加载更多
- 【iOS】UITableView的上拉加载更多和下拉刷新原理
- Swift下拉刷新上拉加载更多库,录音和播放功能,自定义的密码键盘,
- 支持下拉刷新和上划加载更多的自定义RecyclerView(仿XListView效果)
- 自定义Recycerview支持多种类型,下拉刷新,上拉加载更多的适配器
- SwipeRefreshLayout+RecyclerView 完成下拉刷新,上拉加载更多的自定义控件,简单好用
- google官方的下拉刷新+自定义上拉加载更多
- google官方的下拉刷新+自定义上拉加载更多
- 自定义ListView的下拉刷新和上拉加载更多
- Google官方的下拉刷新+自定义上拉加载更多
- YRecyclerView自定义下拉刷新上拉加载更多的RecyclerView
- google官方的下拉刷新+自定义上拉加载更多
- 针对自定义组件上拉刷新下拉加载更多PullToRefreshView的分析(二)
- Android自定义控件(一) 下拉刷新,上拉分页加载更多(支持ListView, GridView, ScrollView)
- listView 模仿ios的上拉刷新下拉加载更多
- 防止SQL注入的五种方法
- maven 使用jrebel热部署,建议用tomcat7
- AI时代-人工智能入学指南
- 著名Web文件管理器elfinder的java servlet后端,支持自定义
- c# -- mysql中的读取数据的几个方法
- IOS控件系列二---优雅的UITableView的MVC模式设计,支持自定义下拉刷新/上提加载更多视图(含swift)
- js的日常
- 硬件能力与智能AI-Zoomla!逐浪CMS2 x3.9.2正式发布
- ELK (Elasticsearch+Logstash+Kibana) 的安装
- 数组排序
- postgresql 函数增加调试功能
- 判断整数的正则表达式
- 浏览器兼容
- 最常用的Unix/Linux命令