本文是一个关于表格的demo,力求极简调用,一句代码,一个表格,如有需要,同时又能实现一定程度的定制,表格中可以放置图片或者文字,单元格具有相应点击的能了,先看gif演示,演示过程有点慢,因为牵涉到几种情况切换时要修改参数:
- 特点:
1、调用极简,一句代码即可搞定;
2、可定制,放置图片或者文字均可;
3、可单独设置单个单元格,或者整体设定所有的单元格;
4、首行标题栏不随表格的滚动而上下滚动,但左右可以滚动,实现表格滚动时标题栏不被隐藏;
5、表格的大小可任意设定,若设定的表格的高度大于所有的单元格的高度之和,则自动缩减表格高度,表格不可滚动;若设定的表格高度小于所有的单元格的高度,则表格可滚动;
6、表格可以相应点击事件。
下载地址:ChartViewDemo 下载地址
- 原理
整体的表格构成思路如下:创建一个view,分为三部分:左边在view左上角放置一个label作为标题(demo中为“事件”),然后往下放置一个tableView,右边上部放置一个scrollview作为首行标题栏,下方先放置一个scrollview,然后在scrollview上放置一个collectionView,滚动的时候实现tableView和scrollview及collectionView的联动。实现方式很多,本文只是其中一种,单元格用button代替也可以实现点击效果。各有利弊吧,本文仅就利用collectionView的实现方式来加以分析。 - 使用
//1、建立左边标题数据
NSArray *leftTitles = @[@"时间",@"09:00-10:00",@"10:00-11:00",@"11:00-12:00",@"12:00-13:00",@"13:00-14:00",@"14:00-15:00",@"15:00-16:00",@"16:00-17:00"];
//2、建立上部标题
NSArray *topTitles = @[@"4月1日",@"4月2日",@"4月3日",@"4月4日",@"4月5日",@"4月6日",@"4月7日",@"4月8日",@"4月9日"];
上面两部为公共部分,无论建立那种表格都这样建立数据,下面则根据你需要创建的表格类型去选择性的使用,共分为四种情况:
(1) 所有单元格为文字
//3-1、如果单元格全部放置文字,则建立要显示的文字数组,按顺序排列(从0行0列开始)
NSMutableArray *allTitles = [[NSMutableArray alloc] init];
for (int i =0; i < (leftTitles.count - 1) * topTitles.count; i ++) {
[allTitles addObject:[NSString stringWithFormat:@"%d",i]];
}
charView = [[ChartView alloc] initWithFrame:CGRectMake(10, 80, kScreenWidth - 20, kscreenHeight - 290) leftTitles:leftTitles topTitles:topTitles itemAllTitleDatas:allTitles];
//若需要单元格的点击事件则需要设置代理,否则不需要
charView.delegate = self;
//将表格添加到view上
[self.view addSubview:charView];
(2) 所有单元格为图片
NSMutableArray *allTitles = [[NSMutableArray alloc] init];
for (int i =0; i < (leftTitles.count - 1) * topTitles.count; i ++) {
[allTitles addObject:[NSString stringWithFormat:@"%d",i]];
}
charView = [[ChartView alloc] initWithFrame:CGRectMake(10, 80, kScreenWidth - 20, kscreenHeight - 290) leftTitles:leftTitles topTitles:topTitles itemAllTitleDatas:allTitles];
//若需要单元格的点击事件则需要设置代理,否则不需要
charView.delegate = self;
//将表格添加到view上
[self.view addSubview:charView];
(3) 单元格部分为文字
//这个时候需要注意,因为是定制的针对个别的单元格,所以需要把该单元格的坐标和要展示的文字的信息给包含进来,这里我采用的是字典的形式,数组中放的全是字典的形式,每个字典代表一个单元格的信息,其中key为该单元格个坐标,格式为:@"横坐标":@"竖坐标"(从0行0列开始),value为要展示的文字
NSArray *itemTitleDatas = @[@{@"0:1":@"item0-1"},@{@"3:3":@"item3-3"}];
charView = [[ChartView alloc] initWithFrame:CGRectMake(10, 80, kScreenWidth - 20, kscreenHeight - 290) leftTitles:leftTitles topTitles:topTitles itemTitleDatas:itemTitleDatas];
//若需要单元格的点击事件则需要设置代理,否则不需要
charView.delegate = self;
//将表格添加到view上
[self.view addSubview:charView];
(4) 单元格部分为图片
//格式同上,不同的地方是字典中的value为图片的名字
NSArray *itemImageDatas = @[@{@"0:1":@"fullOrdered"},@{@"3:3":@"ordered"}];
charView = [[ChartView alloc] initWithFrame:CGRectMake(10, 80, kScreenWidth - 20, kscreenHeight - 290) leftTitles:leftTitles topTitles:topTitles itemImageDatas:itemImageDatas];
如果设置了代理,则可以拿到单元格的点击事件:
-(void)chartViewDidSelecteItemAtRow:(NSInteger)row column:(NSInteger)column{
NSLog(@"选中表格内容:%d行 -- %d列",row,column);
}
-(void)chartViewDidSelecteTitleAtRow:(NSInteger)row{
NSLog(@"选中左边title:%d",row);
}
源码解析
- 1、headerFile
定义了一个ChartDefine.h文件,对于表格的一些字体、颜色、大小的设置等等,可以在这统一修改,注释比较详细,不再赘述:
#define kChartBorderColer [UIColor lightGrayColor].CGColor //表格外框线颜色
#define kLineInsideColer [UIColor lightGrayColor] //表格内部线的颜色
#define kContentTitleColor [UIColor blackColor] //表格内文字的颜色
#define kTopTitleColor [UIColor blackColor] //顶部标题文字颜色
#define kTopLineColor [UIColor lightGrayColor] //顶部标题线的颜色
#define kLeftTopTitleColor [UIColor blackColor] //左边顶部标题文字颜色
#define kLeftTitleColor [UIColor blackColor] //左边标题文字颜色
static CGFloat const kChartBorderLineWidth = 1.0f; //表格外宽线的宽度
static CGFloat const kLeftItemWidth = 100.0f; //左边item宽度
static CGFloat const kTopTitleHeight = 63.0f; //顶部标题栏的高度
static CGFloat const kContentItemWidth = 43.0f; //表格内容item的宽度
static CGFloat const kContentItemHeight = 43.0f; //表格内容item的高度
static CGFloat const kTitleContentSeperateLineWidth = 0.5f;//表格左边标题与表格内容分界线的宽度
static CGFloat const kTitleLineWidth = 0.3f; //顶部标题栏线宽
static CGFloat const kContentLineWidth = 0.25f; //表格内容线的宽度
static CGFloat const kChartCornerRadius = 3.0f; //表格外框的圆角
static CGFloat const kLeftTopTitleFont = 12.0f; //左边顶部标题字体的大小
static CGFloat const kLeftTitleFont = 12.0f; //左边title字体的大小
static CGFloat const kTopTitleFont = 10.0f; //顶部title字体的大小
static CGFloat const kContentTitleFont = 10.0f; //表格内容字体的大小
static NSString *const kLeftTableViewCellId = @"leftTableViewCellId";
static NSString *const kContentCollectionId = @"contentCollectionId";
- 2、头文件
@protocol ChartDelegate;
@interface ChartView : UIView
@property (nonatomic, assign) id<ChartDelegate>delegate;
/**
* 表格内容全是图片
*
* @param frame 表格的frame
* @param leftTitles 左边title数组
* @param topTitles 顶部title数组
* @param allImages 图片数组,直接放图片的名字即可(但是,图片名字的个数不能少于表格的个数,否则会崩溃)
*
* @return ChartView对象
*/
-(instancetype)initWithFrame:(CGRect)frame leftTitles:(NSArray *)leftTitles topTitles:(NSArray *)topTitles itemAllImageDatas:(NSArray *)allImages;
/**
* 表格内容全是文字
*
* @param frame 表格的frame
* @param leftTitles 左边title数组
* @param topTitles 顶部title数组
* @param allTitles 文字数组,直接放需要展示的文字(文字的个数同样不能少于表格的个数,否则会崩溃)
*
* @return ChartView对象
*/
-(instancetype)initWithFrame:(CGRect)frame leftTitles:(NSArray *)leftTitles topTitles:(NSArray *)topTitles itemAllTitleDatas:(NSArray *)allTitles;
/**
* 表格中只有特定的几个表格是图片,其他item为空
*
* @param frame 表格的frame
* @param leftTitles 左边title数组
* @param topTitles 顶部title数组
* @param imageItems 图片数组,格式:数组中全是字典,其中key为该item在表格中的坐标,行和列用“:”隔开,“:”前为行,后为列,表格除开标题外从0行0列开始计算,value为该item对应的图片名称,例:@[@{@"0:1":@"fullOrdered"},@{@"3:3":@"ordered"}],则0为行,1为列,fullOrdered为图片名称
*
* @return ChartView对象
*/
-(instancetype)initWithFrame:(CGRect)frame leftTitles:(NSArray *)leftTitles topTitles:(NSArray *)topTitles itemImageDatas:(NSArray *)imageItems;
/**
* 表格中只有特定的几个表格是文字,其他item为空
*
* @param frame 表格的frame
* @param leftTitles 左边的title数组
* @param topTitles 顶部的title数组
* @param titleItems 文字数组,格式同上,value为该item对应的文字
*
* @return ChartView对象
*/
-(instancetype)initWithFrame:(CGRect)frame leftTitles:(NSArray *)leftTitles topTitles:(NSArray *)topTitles itemTitleDatas:(NSArray *)titleItems;
@end
@protocol ChartDelegate <NSObject>
/**
* 可选,若需要表格中的某个item时的点击事件,则需要设置代理,实现代理方法,否则不需要
*/
@optional
/**
* 选中的某个item
*
* @param row 该item所在的行
* @param column 该item所在的列
*/
-(void)chartViewDidSelecteItemAtRow:(NSInteger )row column:(NSInteger )column;
/**
* 选中的左边title
*
* @param row 选中的左边item的row
*/
-(void)chartViewDidSelecteTitleAtRow:(NSInteger )row;
@end
- .m文件
下面贴出部分核心的代码,具体实现大家可以下载源码:
1、初始化,几个初始化基本一样,在这贴出一个
-(instancetype)initWithFrame:(CGRect)frame leftTitles:(NSArray *)leftTitles topTitles:(NSArray *)topTitles itemAllImageDatas:(NSArray *)allImages{
if (self = [super initWithFrame:frame]) {
_leftData = [NSMutableArray arrayWithArray:leftTitles];
_topTitleData = [NSMutableArray arrayWithArray:topTitles];
_contentAllImages = [NSMutableArray arrayWithArray:allImages];
[self customeChartOutlook];
[self createLeftTableView];
[self createContentCollectionView];
}
return self;
}
2、表格的外观是这样设置的,直接使用的border
-(void)customeChartOutlook{
self.layer.borderWidth = kChartBorderLineWidth;
self.layer.borderColor = kChartBorderColer;
self.layer.cornerRadius = kChartCornerRadius;
self.clipsToBounds = YES;
}
3、左边标题栏
-(void)createLeftTableView{
//第一行标题
UILabel *leftTitle = [[UILabel alloc] initWithFrame:CGRectMake(kChartBorderLineWidth, kChartBorderLineWidth, kLeftItemWidth, kTopTitleHeight)];
leftTitle.text = self.leftData[0];
leftTitle.font = [UIFont systemFontOfSize:kLeftTopTitleFont];
leftTitle.textAlignment = NSTextAlignmentCenter;
leftTitle.textColor = kLeftTopTitleColor;
[self addSubview:leftTitle];
CGFloat tableHeight = kContentItemHeight * (self.leftData.count - 1);
if (self.frame.size.height > tableHeight) {
CGRect tempFrame = self.frame;
tempFrame.size.height = tableHeight;
self.frame = tempFrame;
}else if (self.frame.size.height < tableHeight){
tableHeight = self.frame.size.height;
}
self.leftTableView = [[UITableView alloc] initWithFrame:CGRectMake(kChartBorderLineWidth, kChartBorderLineWidth + kTopTitleHeight, kLeftItemWidth, tableHeight - kTopTitleHeight) style:UITableViewStylePlain];
self.leftTableView.dataSource = self;
self.leftTableView.delegate = self;
self.leftTableView.separatorInset = UIEdgeInsetsZero;
self.leftTableView.layoutMargins = UIEdgeInsetsZero;
self.leftTableView.bounces = NO;
self.leftTableView.showsVerticalScrollIndicator = NO;
self.leftTableView.showsHorizontalScrollIndicator = NO;
[self addSubview:self.leftTableView];
[self.leftTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kLeftTableViewCellId];
UIView *vLine = [[UIView alloc] initWithFrame:CGRectMake(self.leftTableView.frame.origin.x + self.leftTableView.frame.size.width, kChartBorderLineWidth, kTitleContentSeperateLineWidth, self.leftTableView.frame.size.height + kTopTitleHeight + kTitleLineWidth)];
vLine.backgroundColor = kLineInsideColer;
[self addSubview:vLine];
UIView *hLine = [[UIView alloc] initWithFrame:CGRectMake(kChartBorderLineWidth, leftTitle.frame.origin.y + leftTitle.frame.size.height, kLeftItemWidth, kTitleLineWidth)];
hLine.backgroundColor = kTopLineColor;
[self addSubview:hLine];
}
4、右边视图,这个方法写的有点长,分开写更好一点,大家知道思路就可以了,然后可以自己动手写一下
-(void)createContentCollectionView{
CGFloat scrollX = self.leftTableView.frame.origin.x + self.leftTableView.frame.size.width + kTitleContentSeperateLineWidth;
//减去0.5是为了消除黑线,如果调整了黑线的宽度,可以微调这个数来消除黑线
self.contentScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(scrollX - 0.5, self.leftTableView.frame.origin.y - 0.5, self.frame.size.width - kChartBorderLineWidth - scrollX, self.leftTableView.frame.size.height)];
//self.contentScrollView.backgroundColor = [UIColor orangeColor];
self.contentScrollView.contentSize = CGSizeMake(self.contentScrollView.frame.size.width, kContentItemHeight * (self.leftData.count - 1));
self.contentScrollView.delegate = self;
self.contentScrollView.bounces = NO;
self.contentScrollView.showsVerticalScrollIndicator = NO;
[self addSubview:self.contentScrollView];
[self sendSubviewToBack:self.contentScrollView];
UICollectionViewFlowLayout *flow = [[UICollectionViewFlowLayout alloc] init];
[flow setScrollDirection:UICollectionViewScrollDirectionHorizontal];
flow.minimumLineSpacing = 0;
flow.minimumInteritemSpacing = 0;
flow.itemSize = CGSizeMake(kContentItemWidth, kContentItemHeight);
//减去0.2是为了消去黑线
self.contentCollectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0 - 0.2, self.contentScrollView.frame.size.width, kContentItemHeight * (self.leftData.count - 1)) collectionViewLayout:flow];
self.contentCollectionView.backgroundColor = [UIColor whiteColor];
self.contentCollectionView.delegate = self;
self.contentCollectionView.dataSource = self;
self.contentCollectionView.bounces = NO;
self.contentCollectionView.showsVerticalScrollIndicator = NO;
self.contentCollectionView.showsHorizontalScrollIndicator = NO;
[self.contentScrollView addSubview:self.contentCollectionView];
[self.contentCollectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:kContentCollectionId];
//顶部标题栏
self.rightTopScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(self.contentScrollView.frame.origin.x, kChartBorderLineWidth, self.contentScrollView.frame.size.width, kTopTitleHeight + kTitleLineWidth)];
[self addSubview:self.rightTopScrollView];
self.rightTopScrollView.contentSize = CGSizeMake(kContentItemHeight * self.topTitleData.count, 0);
self.rightTopScrollView.showsHorizontalScrollIndicator = NO;
self.rightTopScrollView.bounces = NO;
self.rightTopScrollView.delegate = self;
for (int i = 0; i < self.topTitleData.count; i ++) {
UILabel *rightTopTitle = [[UILabel alloc] initWithFrame:CGRectMake(i * (kContentItemWidth + kTitleLineWidth), 0, kContentItemWidth, kTopTitleHeight)];
rightTopTitle.text = self.topTitleData[i];
rightTopTitle.textAlignment = NSTextAlignmentCenter;
rightTopTitle.font = [UIFont systemFontOfSize:kTopTitleFont];
rightTopTitle.textColor = kTopTitleColor;
rightTopTitle.backgroundColor = [UIColor clearColor];
[self.rightTopScrollView addSubview:rightTopTitle];
//以下坐标算法是为了调整顶部标题栏的竖线与下面内容线不对齐的问题,解决方案很不好,有待完善
UIView *topVLine = [[UIView alloc] initWithFrame:CGRectMake((i + 1) * (kContentItemWidth + kTitleLineWidth) - (i+1) *kContentLineWidth * 1.3, 0, 0.5, kTopTitleHeight)];
topVLine.backgroundColor = kLineInsideColer;
[self.rightTopScrollView addSubview:topVLine];
}
UIView *hLine = [[UIView alloc] initWithFrame:CGRectMake(kChartBorderLineWidth, kTopTitleHeight, self.rightTopScrollView.contentSize.width, kTitleLineWidth)];
hLine.backgroundColor = kTopLineColor;
[self.rightTopScrollView addSubview:hLine];
}
代码就贴这么多吧,主要是项目中用到了表格,需求也是某个单元格有内容,需要响应点击事件,没找到合适的第三方,就自己动手写了一个,由于是基于项目的功能,所以封装的不是特别好,不过对于一般的这种表格需要应该问题不太大了,若发现不足之处欢迎指出!
- 附:
下载地址:ChartViewDemo 下载地址