本文是一個關于表格的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];
}
代碼就貼這么多吧,主要是項目中用到了表格,需求也是某個單元格有內容,需要響應點擊事件,沒找到合適的第三方,就自己動手寫了一個,由于是基于項目的功能,所以封裝的不是特別好,不過對于一般的這種表格需要應該問題不太大了,若發現不足之處歡迎指出!
- 附:
下載地址:ChartViewDemo 下載地址