本文將介紹以及講解
GridView
(網(wǎng)格視圖的實(shí)現(xiàn))
文中所解析的例子已封裝成控件
地址JiaHongXu/JHGridView 歡迎指正和Star~
先上一個(gè)實(shí)現(xiàn)的截圖
準(zhǔn)備階段
前因
這個(gè)項(xiàng)目是前段時(shí)間在老師實(shí)驗(yàn)室接到的一個(gè)公司的協(xié)助開發(fā)項(xiàng)目蛙奖,這是一個(gè)家屑萑伲互通的軟件猿规,里面有一個(gè)功能是需要查看課程表听绳,然而又不希望實(shí)現(xiàn)像超級(jí)課程表那樣的表格視圖惰蜜,于是手動(dòng)封裝了一個(gè)GridView
以滿足需求挺峡。
分析
需要實(shí)現(xiàn)的GridView
需要有如下的功能:
- 背景色和文字顏色可指定枢贿,允許指定某行某列的顏色
- 不同行的行高和列寬可以不同
- 可相應(yīng)點(diǎn)擊事件器罐,點(diǎn)擊某行某列需要作出回應(yīng)(該需求中為點(diǎn)擊課程表中的某個(gè)課程要彈出輸入框,以修改課程名稱)
- 表格可滑動(dòng)抵窒,上下或左右滑動(dòng)(即
contentSize
能超出當(dāng)前View
的大谐谡搿) - 擔(dān)心產(chǎn)品經(jīng)理改動(dòng)需求,表頭(星期一李皇、星期二那行)是否可以跟隨下面的表格一起上下滾動(dòng)削茁,是否固定
- 同樣也是預(yù)防改需求,提前做好文本對(duì)齊方式的修改預(yù)留
思考實(shí)現(xiàn)
- 如果使用
UICollectionView
實(shí)現(xiàn)的話掉房,難以指定某行某列(UICollectionView
的行中item
個(gè)數(shù)是由item
寬度計(jì)算得出茧跋,如需指定一行有幾個(gè)item
需要自己手動(dòng)限制item
大小)卓囚,因此考慮使用UITableView
實(shí)現(xiàn) - 由于需要考慮表頭行是否與表格主體內(nèi)容一起滾動(dòng)瘾杭,因此分離表頭與主體內(nèi)容,主體內(nèi)容使用若干個(gè)
UITableView
構(gòu)成哪亿,表頭使用UIView
橫向嵌套幾個(gè)UILabel
組成 - 需要定制好多東西粥烁,例如對(duì)其方式、字體顏色蝇棉、背景顏色讨阻、字體大小、行高列寬等篡殷,考慮使用代理方法交由使用者指定钝吮,點(diǎn)擊事件可以通過(guò)獲取
UITableView
的代理方法,稍作處理然后交由使用者使用板辽。 - 剩下就是如何對(duì)網(wǎng)格視圖
GridView
中的每個(gè)item賦值的問(wèn)題奇瘦,想過(guò)兩個(gè)解決方案,第一個(gè)是通過(guò)代理方法劲弦,讓用戶實(shí)現(xiàn)一個(gè)返回表格主體每行每列的NSString
和實(shí)現(xiàn)一個(gè)返回表頭每列的NSString
這兩個(gè)方法耳标,另外一個(gè)是直接通過(guò)傳入model
,然后通過(guò)KVC
賦值瓶您,考慮用戶使用方便麻捻,我選擇了第二種
用到的知識(shí)
基本沒啥,主要把去年暑假學(xué)的KVC
拿來(lái)練一下手呀袱,順便復(fù)習(xí)一下代理方法的寫法以及基本控件的使用贸毕,UITableView
的contentSize
相關(guān)知識(shí)以及一些OC的知識(shí),剩下的就是比較繁瑣的封裝了
代碼實(shí)現(xiàn)
定義的枚舉類型和結(jié)構(gòu)體
//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;
分別是用于表示位置坐標(biāo)的GridIndex
(類似NSIndexPath
)夜赵,用于表示選擇樣式的JHGridSelectType
和用于表示對(duì)齊方式的JHGridAlignmentType
可實(shí)現(xiàn)的代理方法
@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;
用于表示標(biāo)題欄是否固定明棍,默認(rèn)為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;
第二個(gè)方法在需要顯示數(shù)據(jù)的時(shí)候調(diào)用,傳入一個(gè)標(biāo)題(表頭行)的NSString
數(shù)組寇僧,一堆需要顯示用的數(shù)據(jù)的model
數(shù)組摊腋,以及每一列對(duì)應(yīng)model
中屬性名稱的NSString
數(shù)組(用于KVC
賦值)
構(gòu)造表格
構(gòu)造表頭沸版,思路是一個(gè)UIView
橫向嵌套若干個(gè)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];
}
構(gòu)造表格主體,通過(guò)一個(gè)UIScrollView
嵌套若干個(gè)UITableView
實(shí)現(xiàn)兴蒸,其中视粮,如果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];
}
其中橙凳,需要手動(dòng)指定UIScrollView
的contentSize
蕾殴,分兩種情況:title
固定(不隨表格體上下滾動(dòng)而滾動(dòng))以及title
不固定(隨表格體上下滾動(dòng)而滾動(dòng))
//setup scrollview's content size
float contentWidth = tableWidth;
float contentHeight = [self isTitleFixed]?tableHeight:tableHeight + [self heightForTitles];
[_backScrollView setContentSize:CGSizeMake(contentWidth, [self isTitleFixed]?tableHeight:contentHeight)];
最后,由于當(dāng)表格主體發(fā)生橫向滾動(dòng)時(shí)岛啸,表頭也需要橫向滾動(dòng)相同的距離钓觉,因此需要設(shè)置聯(lián)動(dòng),通過(guò)實(shí)現(xiàn)scrollViewDidScroll:
方法來(lái)捕獲并處理scrollview
的滾動(dòng)事件
#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
使用該控件時(shí)把JHGridView
目錄拖入工程
如果不需要指定顏色坚踩、字體大小荡灾、行高列寬以及處理選中事件的可以不實(shí)現(xiàn)代理方法
#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];
如果需要自定義,則實(shí)現(xiàn)代理方法
- (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];
}
運(yùn)行效果
完整代碼已上傳至我的Github JiaHongXu/JHGridView 歡迎指正和Star?~
學(xué)生黨一枚瞬铸,以上為本人學(xué)習(xí)中的練習(xí)批幌,若有不足還請(qǐng)各看官大牛指出~
謝謝~~~(づ ̄3 ̄)づ╭?~