iOS的GridView表格视图的实现(课程表Demo)

本文将介绍以及讲解GridView(网格视图的实现)

文中所解析的例子已封装成控件
地址JiaHongXu/JHGridView 欢迎指正和Star~
先上一个实现的截图

GridView实现截图(课程表)

准备阶段

前因

这个项目是前段时间在老师实验室接到的一个公司的协助开发项目,这是一个家校互通的软件,里面有一个功能是需要查看课程表,然而又不希望实现像超级课程表那样的表格视图,于是手动封装了一个GridView以满足需求。

分析

需要实现的GridView需要有如下的功能:

  • 背景色和文字颜色可指定,允许指定某行某列的颜色
  • 不同行的行高和列宽可以不同
  • 可相应点击事件,点击某行某列需要作出回应(该需求中为点击课程表中的某个课程要弹出输入框,以修改课程名称)
  • 表格可滑动,上下或左右滑动(即contentSize能超出当前View的大小)
  • 担心产品经理改动需求,表头(星期一、星期二那行)是否可以跟随下面的表格一起上下滚动,是否固定
  • 同样也是预防改需求,提前做好文本对齐方式的修改预留

思考实现

  • 如果使用UICollectionView实现的话,难以指定某行某列(UICollectionView的行中item个数是由item宽度计算得出,如需指定一行有几个item需要自己手动限制item大小),因此考虑使用UITableView实现
  • 由于需要考虑表头行是否与表格主体内容一起滚动,因此分离表头与主体内容,主体内容使用若干个UITableView构成,表头使用UIView横向嵌套几个UILabel组成
  • 需要定制好多东西,例如对其方式、字体颜色、背景颜色、字体大小、行高列宽等,考虑使用代理方法交由使用者指定,点击事件可以通过获取UITableView的代理方法,稍作处理然后交由使用者使用。
  • 剩下就是如何对网格视图GridView中的每个item赋值的问题,想过两个解决方案,第一个是通过代理方法,让用户实现一个返回表格主体每行每列的NSString和实现一个返回表头每列的NSString这两个方法,另外一个是直接通过传入model,然后通过KVC赋值,考虑用户使用方便,我选择了第二种

用到的知识

基本没啥,主要把去年暑假学的KVC拿来练一下手,顺便复习一下代理方法的写法以及基本控件的使用,UITableViewcontentSize相关知识以及一些OC的知识,剩下的就是比较繁琐的封装了


代码实现

定义的枚举类型和结构体

//use for representing the location in gridview
typedef struct GridIndex {
    long row;
    long col;
} GridIndex;

//use for specifing the selection type
typedef enum{
    JHGridSelectTypeNone,       //default, none selection can be seen
    JHGridSelectTypeDefault,    //when click on an item, the whole line will be selected also
    JHGridSelectTypeSingle,     //only select the clicked item
}JHGridSelectType;

//use for specifing the alignment type
typedef enum{
    JHGridAlignmentTypeDefault, //center alignment
    JHGridAlignmentTypeLeft,    //left alignment
    JHGridAlignmentTypeRight,   //right alignment
}JHGridAlignmentType;

分别是用于表示位置坐标的GridIndex(类似NSIndexPath),用于表示选择样式的JHGridSelectType和用于表示对齐方式的JHGridAlignmentType

可实现的代理方法

@protocol JHGridViewDelegate<NSObject>

@optional
- (CGFloat)heightForRowAtIndex:(long)index;
@optional
- (CGFloat)widthForColAtIndex:(long)index;
@optional
- (CGFloat)heightForTitles;
@optional
- (BOOL)isTitleFixed;
@optional
- (void)didSelectRowAtGridIndex:(GridIndex)gridIndex;
@optional
- (JHGridSelectType)gridViewSelectType;
@optional
- (JHGridAlignmentType)gridViewAlignmentType;
@optional
- (UIColor *)backgroundColorForTitleAtIndex:(long)index;
@optional
- (UIColor *)backgroundColorForGridAtGridIndex:(GridIndex)gridIndex;
@optional
- (UIColor *)textColorForTitleAtIndex:(long)index;
@optional
- (UIColor *)textColorForGridAtGridIndex:(GridIndex)gridIndex;
@optional
- (UIFont *)fontForTitleAtIndex:(long)index;
@optional
- (UIFont *)fontForGridAtGridIndex:(GridIndex)gridIndex;
@end

其中比较常用的有

- (void)didSelectRowAtGridIndex:(GridIndex)gridIndex;

用于处理表格元素被选中的事件

- (BOOL)isTitleFixed;

用于表示标题栏是否固定,默认为NO

JHGridView的方法

//init methods
- (instancetype)initWithFrame:(CGRect)frame;

//call this methods to load the gridview
- (void)setTitles:(NSArray<NSString *> *)titles andObjects:(NSArray *)objects withTags:(NSArray<NSString *> *)tags;

第二个方法在需要显示数据的时候调用,传入一个标题(表头行)的NSString数组,一堆需要显示用的数据的model数组,以及每一列对应model中属性名称的NSString数组(用于KVC赋值)

构造表格

构造表头,思路是一个UIView横向嵌套若干个UILabel

//setup titles
float offsetX = 0;
for (long i = 0; i < _titles.count; i++) {
    gridWidth = [self widthForColAtIndex:i];
    CGRect frame = CGRectMake(offsetX, 0, gridWidth, titleHeight);
    offsetX += gridWidth;
    UIView *titleView = [[UIView alloc] initWithFrame:frame];
    titleView.layer.borderWidth = 0.5;
    titleView.layer.borderColor = [[UIColor groupTableViewBackgroundColor] CGColor];
    titleView.backgroundColor = [self backgroundColorForTitleAtIndex:i];
    UILabel *titleLbl = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
    titleLbl.text = [_titles objectAtIndex:i];
    titleLbl.textColor = [self textColorForTitleAtIndex:i];
    [titleLbl setFont:[self fontSizeForTitleAtIndex:i]];
    [self applyAlignmentTypeFor:titleLbl];
    [titleView addSubview:titleLbl];
    [_backTitleScrollView addSubview:titleView];
}

构造表格主体,通过一个UIScrollView嵌套若干个UITableView实现,其中,如果title不固定,需要把titleView也给addscrollViewsubview中去

//setup tables
float offsetX = 0;
for (long i = 0; i < titleNum; i++) {
    CGRect frame = CGRectZero;
    gridWidth = [self widthForColAtIndex:i];
    if ([self isTitleFixed]) {
        frame = CGRectMake(offsetX, 0, gridWidth, tableHeight);
    }else{
        frame = CGRectMake(offsetX, [self heightForTitles], gridWidth, tableHeight);
    }
    offsetX += gridWidth;
    UITableView *tableView = [[UITableView alloc] initWithFrame:frame style:UITableViewStylePlain];
    tableView.layer.borderWidth = 0.5;
    tableView.layer.borderColor = [[UIColor groupTableViewBackgroundColor] CGColor];
    tableView.separatorInset = UIEdgeInsetsMake(0, 0, 0, 0);
    tableView.scrollEnabled = NO;
    tableView.tag = i;
    tableView.delegate = self;
    tableView.dataSource = self;
    [_backScrollView addSubview:tableView];
}

其中,需要手动指定UIScrollViewcontentSize,分两种情况:title固定(不随表格体上下滚动而滚动)以及title不固定(随表格体上下滚动而滚动)

//setup scrollview's content size
float contentWidth = tableWidth;
float contentHeight = [self isTitleFixed]?tableHeight:tableHeight + [self heightForTitles];
[_backScrollView setContentSize:CGSizeMake(contentWidth, [self isTitleFixed]?tableHeight:contentHeight)];

最后,由于当表格主体发生横向滚动时,表头也需要横向滚动相同的距离,因此需要设置联动,通过实现scrollViewDidScroll:方法来捕获并处理scrollview的滚动事件

#pragma mark --Self ScrollView Delegate Methods
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    if ([scrollView isEqual:_backTitleScrollView]) {
        CGPoint offset = _backScrollView.contentOffset;
        offset.x = _backTitleScrollView.contentOffset.x;
        [_backScrollView setContentOffset:offset];
    }else{
        CGPoint offset = _backTitleScrollView.contentOffset;
        offset.x = _backScrollView.contentOffset.x;
        [_backTitleScrollView setContentOffset:offset];
    }
}

使用Demo

使用该控件时把JHGridView目录拖入工程
如果不需要指定颜色、字体大小、行高列宽以及处理选中事件的可以不实现代理方法

#import "JHGridView.h"
@interface ViewController ()<JHGridViewDelegate>

初始化并赋值

JHGridView *gridView = [[JHGridView alloc] initWithFrame:
                        CGRectMake(0, 64, self.view.frame.size.width, 300)];
gridView.delegate = self;
NSArray *array = @[
                   [[TestObject alloc] initWithName:@"aaa" Sex:@"11" Number:@"ooo" Address:@"sdfabfsakjbakf" Birthday:@"1996-01-01"],
                   [[TestObject alloc] initWithName:@"aaa" Sex:@"11" Number:@"ooo" Address:@"sdfabfsakjbakf" Birthday:@"1996-01-02"],
                   [[TestObject alloc] initWithName:@"aaa" Sex:@"11" Number:@"ooo" Address:@"sdfabfsakjbakf" Birthday:@"1996-01-03"],
                   [[TestObject alloc] initWithName:@"aaa" Sex:@"11" Number:@"ooo" Address:@"sdfabfsakjbakf" Birthday:@"1996-01-04"],
                   [[TestObject alloc] initWithName:@"aaa" Sex:@"11" Number:@"ooo" Address:@"sdfabfsakjbakf" Birthday:@"1996-01-05"],
                   [[TestObject alloc] initWithName:@"aaa" Sex:@"11" Number:@"ooo" Address:@"sdfabfsakjbakf" Birthday:@"1996-01-06"],
                   [[TestObject alloc] initWithName:@"aaa" Sex:@"11" Number:@"ooo" Address:@"sdfabfsakjbakf" Birthday:@"1996-01-07"],
                   [[TestObject alloc] initWithName:@"aaa" Sex:@"11" Number:@"ooo" Address:@"sdfabfsakjbakf" Birthday:@"1996-01-08"],
                   [[TestObject alloc] initWithName:@"aaa" Sex:@"11" Number:@"ooo" Address:@"sdfabfsakjbakf" Birthday:@"1996-01-09"],
                   [[TestObject alloc] initWithName:@"aaa" Sex:@"11" Number:@"ooo" Address:@"sdfabfsakjbakf" Birthday:@"1996-01-10"],
                   [[TestObject alloc] initWithName:@"aaa" Sex:@"11" Number:@"ooo" Address:@"sdfabfsakjbakf" Birthday:@"1996-01-11"]];
[gridView setTitles:@[@"NAME",
                      @"SEX",
                      @"PHONE",
                      @"ADDRESS",
                      @"BIRTHDAY"]
         andObjects:array withTags:@[@"name",@"sex",@"number",@"address",@"birthday"]];
[self.view addSubview:gridView];

如果需要自定义,则实现代理方法

- (void)didSelectRowAtGridIndex:(GridIndex)gridIndex{
    NSLog(@"selected at\ncol:%ld -- row:%ld", gridIndex.col, gridIndex.row);
}

- (BOOL)isTitleFixed{
    return YES;
}

- (CGFloat)widthForColAtIndex:(long)index{
    if (index==3||index==4) {
        return 120;
    }else{
        return 90;
    }
}

- (UIColor *)backgroundColorForTitleAtIndex:(long)index{
    return [UIColor colorWithRed:229/255.0 green:114/255.0 blue:30/255.0 alpha:1];
}

- (UIColor *)backgroundColorForGridAtGridIndex:(GridIndex)gridIndex{
    if (gridIndex.row == 2) {
        return [UIColor cyanColor];
    }else if (gridIndex.col == 4){
        return [UIColor yellowColor];
    }else{
        return [UIColor whiteColor];
    }
}

- (UIColor *)textColorForTitleAtIndex:(long)index{
    if (index==1) {
        return [UIColor whiteColor];
    }else{
        return [UIColor blackColor];
    }
}

- (UIColor *)textColorForGridAtGridIndex:(GridIndex)gridIndex{
    if (gridIndex.col == 1) {
        return [UIColor blueColor];
    }else{
        return [UIColor blackColor];
    }
}

- (UIFont *)fontForTitleAtIndex:(long)index{
    return [UIFont systemFontOfSize:20];
}

运行效果


Demo运行截图

完整代码已上传至我的Github JiaHongXu/JHGridView 欢迎指正和Star✨~
学生党一枚,以上为本人学习中的练习,若有不足还请各看官大牛指出~
谢谢~~~(づ ̄3 ̄)づ╭❤~

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

推荐阅读更多精彩内容

  • 概述在iOS开发中UITableView可以说是使用最广泛的控件,我们平时使用的软件中到处都可以看到它的影子,类似...
    liudhkk阅读 9,062评论 3 38
  • 翻译自“Collection View Programming Guide for iOS” 0 关于iOS集合视...
    lakerszhy阅读 3,871评论 1 22
  • 我想,世界十大禁曲人们最熟知的应该是《黑色星期天》,接下来我们细数世界十大禁曲都有那些。 世界十大禁曲其实只有三首...
    菲烟笑阅读 2,741评论 0 2
  • 夜降临了,天边的云还在剧烈的翻滚着,昭示着暴风雨存在的痕迹。一只猫头鹰站在树干上,锐利的眼睛警惕的盯着四周,偶尔伸...
    燕落亭阅读 438评论 0 2
  • 7.0版本的“时刻"是陌陌的不断走强化社交效率和尝试新趋势的设计,“美好的时刻一定要和朋友分享”这句Slogan体...
    曹大白阅读 1,522评论 0 2