一句代码创建表格

本文是一个关于表格的demo,力求极简调用,一句代码,一个表格,如有需要,同时又能实现一定程度的定制,表格中可以放置图片或者文字,单元格具有相应点击的能了,先看gif演示,演示过程有点慢,因为牵涉到几种情况切换时要修改参数:

ChartViewDemo.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];
}

代码就贴这么多吧,主要是项目中用到了表格,需求也是某个单元格有内容,需要响应点击事件,没找到合适的第三方,就自己动手写了一个,由于是基于项目的功能,所以封装的不是特别好,不过对于一般的这种表格需要应该问题不太大了,若发现不足之处欢迎指出!


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容