之前做项目一直使用自己封装TableView框架,最近把他整理了下(不要脸的)开放出来。如果您用的比较爽的话请点击右上角的star关注下,也可以随时发送issues吐槽给我,我会随时发现并解决。
github仓库地址
技术特点
- 无需继承自定义类,引入头文件UIViewController+GYTableView.h即可使用
- Section和Cell层次更加清晰,根据传入的Section数据结构内部已经全部实现Section和Cell相关delegate方法
- Cell实例可获得外部动态数据,索引位置,上下关系,选中状态等,随时更换样式
- 自带MJRefresh框架,提供下拉刷新和上拉加载功能,外部暴露接口调用
- 提供Section,Cell间距设置,提供选中行高亮、选中行自动居中,提供设置Cell动态高度设置等API
- 框架中的元素全部继承于原生的tableView相关元素,除部分代理方法外,其他原生方法扔然可以使用
安装方法
- pod安装: pod ‘GYTableViewController’
- 手动安装:手动安装需要添加两个库,将GYTableViewController项目文件中Framework文件下的文件导入自身项目,同时此框架基于MJRefresh,所以也需要导入MJRefresh框架文件,手动或者pod都可以,MJRefresh安装方法
框架用法
请使用该框架中的元素来代替原生列表控件,对应关系如下:
GYTableBaseView -> UITableView
UIViewController+GYTableView -> UIViewController
GYTableViewController(废弃,使用UIViewController+GYTableView代替) -> UITableViewController
GYTableViewCell -> UITableViewCell
GYTableViewSection 原生使用UIView展示section内容,这里使用GYTableViewSection
SectionVo 用来设置Section样式与GYTableViewSection实例绑定
CellVo 用来设置Cell样式与GYTableViewCell实例绑定
使用时有列表控件的界面直接引入头文件UIViewController+GYTableView.h即可,.h示例如下
#import "UIViewController+GYTableView.h"
@interface NormalViewController : UIViewController
.m文件必须开启useGYTableView开关来使用表格控件GYTableView
- (BOOL)useGYTableView {
return YES;
}
.m文件中重写headerRefresh添加元素,当自带的下拉刷新控件下拉时调用;从而开始列表内容层次搭建,以及各种类型的Cell位置如何摆放等
- (void)headerRefresh:(GYTableBaseView *)tableView endRefreshHandler:(HeaderRefreshHandler)endRefreshHandler {
//下拉刷新后开始请求后台提供数据,请求到数据后根据解析的内容展开cell实例和位置等操作,代码结构如下(伪代码)
request{
tableView{
sectionVo{
cellVo,
cellVo,
...
}
sectionVo{
cellVo,
...
}
...
}
endRefreshHandler();//界面搭建完毕后停止刷新
}
}
添加Cell
列表控制器内部实现
-(void)headerRefresh:(GYTableBaseView *)tableView endRefreshHandler:(HeaderRefreshHandler)endRefreshHandler{
[tableView addSectionVo:[SectionVo initWithParams:^(SectionVo *svo) {
//添加一个高度为230,类型为BannerViewCell,展示banner图片列表的Cell
[svo addCellVo:[CellVo initWithParams:230 cellClass:RefreshBannerViewCell.class cellData:self.bannerUrlGroup]];
}]];
endRefreshHandler(YES);//不要忘了结束刷新,否则刷新动画会停留原地
}
批量添加Cell
列表控制器内部实现
-(void)headerRefresh:(GYTableBaseView *)tableView endRefreshHandler:(HeaderRefreshHandler)endRefreshHandler{
[tableView addSectionVo:[SectionVo initWithParams:^(SectionVo *svo) {
//添加一个高度为230,类型为BannerViewCell,展示banner图片列表的Cell
[svo addCellVo:[CellVo initWithParams:230 cellClass:RefreshBannerViewCell.class cellData:self.bannerUrlGroup]];
}]];
//注意banner和基金产品列表属于不同区域,应存放到各自section中添加,管理section视图会比较方便
[tableView addSectionVo:[SectionVo initWithParams:^(SectionVo *svo) {
//添加多个高度为80,类型为RefreshFundViewCell,展示基金信息的Cell
[svo addCellVo:[CellVo initWithParams:80 cellClass:RefreshFundViewCell.class cellData:self.fundModels[0]]];
[svo addCellVo:[CellVo initWithParams:80 cellClass:RefreshFundViewCell.class cellData:self.fundModels[1]]];
[svo addCellVo:[CellVo initWithParams:80 cellClass:RefreshFundViewCell.class cellData:self.fundModels[2]]];
//...
}]];
endRefreshHandler(YES);//不要忘了结束刷新,否则刷新动画会停留原地
}
相同类型的Cell添加可以修改成通过原数组批量添加
[tableView addSectionVo:[SectionVo initWithParams:^(SectionVo *svo) {
//添加多个高度为80,类型为RefreshFundViewCell,展示基金信息的Cell
[svo addCellVoByList:[CellVo dividingCellVoBySourceArray:80 cellClass:RefreshFundViewCell.class sourceArray:self.fundModels]];
}]];
添加Section
如果一节内容需要添加section页眉视图,只要在sectionVo实例设置sectionHeaderClass即可,同理section页脚设置sectionFooterClass
-(void)headerRefresh:(GYTableBaseView *)tableView endRefreshHandler:(HeaderRefreshHandler)endRefreshHandler{
[tableView addSectionVo:[SectionVo initWithParams:36 sectionHeaderClass:RefreshFundViewSection.class sectionHeaderData:@"精品专区" nextBlock:^(SectionVo *svo) {
//添加section内的cell...
}]];
endRefreshHandler(YES);
}
分类结构如下
isUnique唯一性
默认所有相同Class的Cell实例都是相互复用,每次下拉刷新或者table设置reloadData,被复用的Cell实例都会重新触发刷新调用showSubviews,从而根据传递的data展开;然而,一些特殊的Cell不需要复用或只实例化一次,比如标签按钮区域的Cell或者banner区域的Cell,每次下拉都是只用这个实例,可以设置为isUnique作为唯一Cell实例优化提高性能
-(void)headerRefresh:(GYTableBaseView *)tableView endRefreshHandler:(HeaderRefreshHandler)endRefreshHandler{
[tableView addSectionVo:[SectionVo initWithParams:^(SectionVo *svo) {
//添加一个高度为230,类型为BannerViewCell,展示banner图片列表的Cell
[svo addCellVo:[CellVo initWithParams:230 cellClass:RefreshBannerViewCell.class cellData:self.bannerUrlGroup isUnique:YES]];
//添加一个高度为90,类型为RefreshHotViewCell,展示banner图片列表的Cell
[svo addCellVo:[CellVo initWithParams:90 cellClass:RefreshHotViewCell.class cellData:self.hotModels isUnique:YES]];
}]];
endRefreshHandler(YES);
}
调用上拉加载
列表控制器内部设置显示上拉加载控制器
-(BOOL)isShowFooter{
return YES;
}
列表控制器内部重写footerLoadMore
//endLoadMoreHandler:结束刷新回调block,lastSectionVo:上一节sectionVo数据,即当前列表页最后一节
-(void)footerLoadMore:(GYTableBaseView *)tableView endLoadMoreHandler:(FooterLoadMoreHandler)endLoadMoreHandler lastSectionVo:(SectionVo *)lastSectionVo{
[lastSectionVo addCellVoByList:[CellVo dividingCellVoBySourceArray:80 cellClass:RefreshFundViewCell.class sourceArray:self.fundNewModels]];//将新增的CellVo实例继续添加到上一节SectionVo实例中
endLoadMoreHandler(YES);
}
根据需求添加到列表页最后一节,或者添加到新的一节数据中,并设置添加上限
if([tableView getTotalCellVoCount] > 30){//总共超出30条数据不添加数据
endLoadMoreHandler(NO);//直接结束上拉加载刷新,并显示"已经全部加载完毕"
return;
}
//根据业务需求的不同,可以继续添加到上一节sectionVo,也可以添加到新的一节sectionVo中
if([lastSectionVo getCellVoCount] < 15){//上一节少于15条继续添加到上一节sectionVo
[lastSectionVo addCellVoByList:[CellVo dividingCellVoBySourceArray:80 cellClass:RefreshFundViewCell.class sourceArray:self.fundNewModels]];
}else{//上一节超了 添加到新的一节sectionVo
[tableView addSectionVo:[SectionVo initWithParams:36 sectionHeaderClass:RefreshFundViewSection.class sectionHeaderData:@"推荐专区" nextBlock:^(SectionVo *svo) {
[svo addCellVoByList:[CellVo dividingCellVoBySourceArray:80 cellClass:RefreshFundViewCell.class sourceArray:self.fundNewModels]];
}]];
}
endLoadMoreHandler(YES);//不要忘了结束上拉加载刷新
更改UITableView的frame
列表控制器内部重写getTableViewFrame
如存在和容器底部对齐的元素,请在此方法对齐底部位置(默认占满controller边界);autoLayerout无需重写此方法,自行设置tableView和其他元素布局关系
-(CGRect)getTableViewFrame{
self.noticeBack.frame = CGRectMake(0, 0, self.view.width, 30);
self.submitButton.maxY = self.view.height;//底部按钮对齐容器底部
//返回设置好的tableView位置frame 高度=总高度-公告区高-底部按钮高
return CGRectMake(0, self.noticeBack.height, self.view.width, self.view.height - self.noticeBack.height - self.submitButton.height);
}
自定义下拉刷新控件
列表控制器内部重写getRefreshHeader
-(MJRefreshHeader *)getRefreshHeader{
return [[DiyRotateRefreshHeader alloc]init];
}
侦听选中的Cell
列表控制器内部实现代理 (tableView:didSelectRowAtIndexPath:已废弃)
- (void)didSelectRow:(GYTableBaseView *)tableView indexPath:(NSIndexPath *)indexPath {
CellVo* cvo = [tableView getCellVoByIndexPath:indexPath];//获取到绑定的CellVo
XXClass* cellData = cvo.cellData;//获得cell的原始数据
//根据数据添加业务逻辑...
}
设置cell点击效果,cell实例内部重写showSelectionStyle
- (BOOL)showSelectionStyle {
return YES;
}
设置Cell或Section元素间距
列表控制器内部设置tableView属性cellGap或sectionGap
- (void)viewDidLoad {
self.tableView.sectionGap = 6;//设置每一节区域之间间距
self.tableView.cellGap = 3;//设置每个Cell之间间距(包含每一节区域)
}
设置选中某个位置的Cell
当刷新完成后设置,列表控制器内部设置tableView属性selectedIndexPath
-(void)headerRefresh:(GYTableBaseView *)tableView endRefreshHandler:(HeaderRefreshHandler)endRefreshHandler{
[tableView addSectionVo:[SectionVo initWithParams:^(SectionVo *svo) {
//..添加cell数据
}]];
tableView.selectedIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];//设置选中某个indexPath
endRefreshHandler(YES);
}
Cell实例设置选中效果,重写setSelected方法,选中样式请根据需求自行添加
-(void)setSelected:(BOOL)selected{
[super setSelected:selected];
[self checkCellRelate];//自定义选中样式方法,非框架内部方法,实现如下
}
Cell实例位置关系isFirst,isLast,位于第一个或最后一个和中间段的Cell样式不同
-(void)checkCellRelate{
if (self.isFirst) {
[self drawFirstStyle:nodeColor];
}else if(self.isLast){
[self drawLastStyle:nodeColor];
}else{
[self drawNormalStyle:nodeColor];
}
}
设置交互点击某个位置Cell并高亮
- (void)viewDidLoad {
self.tableView.clickCellHighlight = YES;
}
设置点击Cell自动居中
- (void)viewDidLoad {
self.tableView.clickCellMoveToCenter = YES;
}
Cell自动调整高度
列表控制器内部设置CellVo传入高度CELL_AUTO_HEIGHT
-(void)headerRefresh:(GYTableBaseView *)tableView endRefreshHandler:(HeaderRefreshHandler)endRefreshHandler{
[tableView addSectionVo:[SectionVo initWithParams:^(SectionVo *svo) {
[svo addCellVoByList:[CellVo dividingCellVoBySourceArray:CELL_AUTO_HEIGHT cellClass:AutoHeightWeiboCell.class sourceArray:self.weiboModels]];
}]];
endRefreshHandler(YES);
}
Cell实例重写getCellHeight方法获取动态高度,获取高度内容会被缓存不会二次计算
-(CGFloat)getCellHeight:(CGFloat)cellWidth{
WeiboModel* weiboModel = GET_CELL_DATA(WeiboModel.class);//获取Model
NSString* content = weiboModel.content;//获取动态内容字符串
CGRect contentSize = [content boundingRectWithSize:CGSizeMake(cellWidth - LEFT_PADDING - RIGHT_PADDING, FLT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:SIZE_TEXT_SECONDARY]}
context:nil];//计算给定范围内最佳尺寸
return TOPIC_AREA_HEIGHT + contentSize.size.height + IMAGE_AREA_HEIGHT + BOTTOM_PADDING * 2;//返回计算后的最终高度
}