美文网首页iOS自定义控件与效果iOS技术专题别人的iOS精华
iOS的GridView表格视图的实现(课程表Demo)

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

作者: 箪食豆羹 | 来源:发表于2017-01-07 15:58 被阅读3779次

    本文将介绍以及讲解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 ̄)づ╭❤~

    相关文章

      网友评论

      • 小冯哥:你好,请问下,代码中,tableview的代理方法,对高度的设置,好像不会执行,不管你对每行的高度设置为多少,都会是默认的高度44,只有title的高度会设置成功,为什么?
      • ft6206:为什么不用UIcollectionview实现呢 这样写明显效率很低啊
        箪食豆羹:@ft6206 :smile: 当时年少~近期再研究研究~谢谢大佬建议
      • 风停了_123:第一张的实现截图怎么弄啊?
      • 4f2931dd00b5:谢谢分享,刚好有需要做类似的表格
      • SAW_:好东西,顶!只是你这个代理里面写这么多@optional不累吗:sweat:
      • 大头herob:请问, 表头可以固定, 左边第一栏可以固定吗?
        箪食豆羹:@herob :grin:好建议~今后会加入的
      • a0cc62b90377:厉害了!
      • afe72446ec06:好棒啊^_^ 刚好不会写呢哈哈哈

      本文标题:iOS的GridView表格视图的实现(课程表Demo)

      本文链接:https://www.haomeiwen.com/subject/fvdfbttx.html