本文将介绍以及讲解
GridView
(网格视图的实现)
文中所解析的例子已封装成控件
地址JiaHongXu/JHGridView 欢迎指正和Star~
先上一个实现的截图
准备阶段
前因
这个项目是前段时间在老师实验室接到的一个公司的协助开发项目,这是一个家校互通的软件,里面有一个功能是需要查看课程表,然而又不希望实现像超级课程表那样的表格视图,于是手动封装了一个GridView
以满足需求。
分析
需要实现的GridView
需要有如下的功能:
- 背景色和文字颜色可指定,允许指定某行某列的颜色
- 不同行的行高和列宽可以不同
- 可相应点击事件,点击某行某列需要作出回应(该需求中为点击课程表中的某个课程要弹出输入框,以修改课程名称)
- 表格可滑动,上下或左右滑动(即
contentSize
能超出当前View
的大小) - 担心产品经理改动需求,表头(星期一、星期二那行)是否可以跟随下面的表格一起上下滚动,是否固定
- 同样也是预防改需求,提前做好文本对齐方式的修改预留
思考实现
- 如果使用
UICollectionView
实现的话,难以指定某行某列(UICollectionView
的行中item
个数是由item
宽度计算得出,如需指定一行有几个item
需要自己手动限制item
大小),因此考虑使用UITableView
实现 - 由于需要考虑表头行是否与表格主体内容一起滚动,因此分离表头与主体内容,主体内容使用若干个
UITableView
构成,表头使用UIView
横向嵌套几个UILabel
组成 - 需要定制好多东西,例如对其方式、字体颜色、背景颜色、字体大小、行高列宽等,考虑使用代理方法交由使用者指定,点击事件可以通过获取
UITableView
的代理方法,稍作处理然后交由使用者使用。 - 剩下就是如何对网格视图
GridView
中的每个item赋值的问题,想过两个解决方案,第一个是通过代理方法,让用户实现一个返回表格主体每行每列的NSString
和实现一个返回表头每列的NSString
这两个方法,另外一个是直接通过传入model
,然后通过KVC
赋值,考虑用户使用方便,我选择了第二种
用到的知识
基本没啥,主要把去年暑假学的KVC
拿来练一下手,顺便复习一下代理方法的写法以及基本控件的使用,UITableView
的contentSize
相关知识以及一些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
也给add
到scrollView
的subview
中去
//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];
}
其中,需要手动指定UIScrollView
的contentSize
,分两种情况: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 ̄)づ╭❤~
网友评论