You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

346 lines
10 KiB

  1. //
  2. // PTMultiColumnTableView.m
  3. // PTMultiColumnTableViewDemo
  4. //
  5. // Created by Peng Tao on 15/11/16.
  6. // Copyright © 2015Peng Tao. All rights reserved.
  7. //
  8. #import "FLEXMultiColumnTableView.h"
  9. #import "FLEXTableContentCell.h"
  10. #import "FLEXTableLeftCell.h"
  11. @interface FLEXMultiColumnTableView ()
  12. <UITableViewDataSource, UITableViewDelegate,UIScrollViewDelegate, FLEXTableContentCellDelegate>
  13. @property (nonatomic) UIScrollView *contentScrollView;
  14. @property (nonatomic) UIScrollView *headerScrollView;
  15. @property (nonatomic) UITableView *leftTableView;
  16. @property (nonatomic) UITableView *contentTableView;
  17. @property (nonatomic) UIView *leftHeader;
  18. @property (nonatomic) NSDictionary<NSString *, NSNumber *> *sortStatusDict;
  19. @property (nonatomic) NSArray *rowData;
  20. @end
  21. static const CGFloat kColumnMargin = 1;
  22. @implementation FLEXMultiColumnTableView
  23. - (instancetype)initWithFrame:(CGRect)frame
  24. {
  25. self = [super initWithFrame:frame];
  26. if (self) {
  27. [self loadUI];
  28. }
  29. return self;
  30. }
  31. - (void)didMoveToSuperview
  32. {
  33. [super didMoveToSuperview];
  34. [self reloadData];
  35. }
  36. - (void)layoutSubviews
  37. {
  38. [super layoutSubviews];
  39. CGFloat width = self.frame.size.width;
  40. CGFloat height = self.frame.size.height;
  41. CGFloat topheaderHeight = [self topHeaderHeight];
  42. CGFloat leftHeaderWidth = [self leftHeaderWidth];
  43. CGFloat topInsets = 0.f;
  44. if (@available (iOS 11.0, *)) {
  45. topInsets = self.safeAreaInsets.top;
  46. }
  47. CGFloat contentWidth = 0.0;
  48. NSInteger rowsCount = [self numberOfColumns];
  49. for (int i = 0; i < rowsCount; i++) {
  50. contentWidth += [self contentWidthForColumn:i];
  51. }
  52. self.leftTableView.frame = CGRectMake(0, topheaderHeight + topInsets, leftHeaderWidth, height - topheaderHeight - topInsets);
  53. self.headerScrollView.frame = CGRectMake(leftHeaderWidth, topInsets, width - leftHeaderWidth, topheaderHeight);
  54. self.headerScrollView.contentSize = CGSizeMake( self.contentTableView.frame.size.width, self.headerScrollView.frame.size.height);
  55. self.contentTableView.frame = CGRectMake(0, 0, contentWidth + [self numberOfColumns] * [self columnMargin] , height - topheaderHeight - topInsets);
  56. self.contentScrollView.frame = CGRectMake(leftHeaderWidth, topheaderHeight + topInsets, width - leftHeaderWidth, height - topheaderHeight - topInsets);
  57. self.contentScrollView.contentSize = self.contentTableView.frame.size;
  58. self.leftHeader.frame = CGRectMake(0, topInsets, [self leftHeaderWidth], [self topHeaderHeight]);
  59. }
  60. - (void)loadUI
  61. {
  62. [self loadHeaderScrollView];
  63. [self loadContentScrollView];
  64. [self loadLeftView];
  65. }
  66. - (void)reloadData
  67. {
  68. [self loadLeftViewData];
  69. [self loadContentData];
  70. [self loadHeaderData];
  71. }
  72. #pragma mark - UI
  73. - (void)loadHeaderScrollView
  74. {
  75. UIScrollView *headerScrollView = [UIScrollView new];
  76. headerScrollView.delegate = self;
  77. self.headerScrollView = headerScrollView;
  78. self.headerScrollView.backgroundColor = [UIColor colorWithWhite:0.803 alpha:0.850];
  79. [self addSubview:headerScrollView];
  80. }
  81. - (void)loadContentScrollView
  82. {
  83. UIScrollView *scrollView = [UIScrollView new];
  84. scrollView.bounces = NO;
  85. scrollView.delegate = self;
  86. UITableView *tableView = [UITableView new];
  87. tableView.delegate = self;
  88. tableView.dataSource = self;
  89. tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  90. [self addSubview:scrollView];
  91. [scrollView addSubview:tableView];
  92. self.contentScrollView = scrollView;
  93. self.contentTableView = tableView;
  94. }
  95. - (void)loadLeftView
  96. {
  97. UITableView *leftTableView = [UITableView new];
  98. leftTableView.delegate = self;
  99. leftTableView.dataSource = self;
  100. leftTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  101. self.leftTableView = leftTableView;
  102. [self addSubview:leftTableView];
  103. UIView *leftHeader = [UIView new];
  104. leftHeader.backgroundColor = [UIColor colorWithWhite:0.950 alpha:0.668];
  105. self.leftHeader = leftHeader;
  106. [self addSubview:leftHeader];
  107. }
  108. #pragma mark - Data
  109. - (void)loadHeaderData
  110. {
  111. NSArray<UIView *> *subviews = self.headerScrollView.subviews;
  112. for (UIView *subview in subviews) {
  113. [subview removeFromSuperview];
  114. }
  115. CGFloat x = 0.0;
  116. CGFloat w = 0.0;
  117. for (int i = 0; i < [self numberOfColumns] ; i++) {
  118. w = [self contentWidthForColumn:i] + [self columnMargin];
  119. FLEXTableColumnHeader *cell = [[FLEXTableColumnHeader alloc] initWithFrame:CGRectMake(x, 0, w, [self topHeaderHeight] - 1)];
  120. cell.label.text = [self columnTitleForColumn:i];
  121. [self.headerScrollView addSubview:cell];
  122. FLEXTableColumnHeaderSortType type = [self.sortStatusDict[[self columnTitleForColumn:i]] integerValue];
  123. [cell changeSortStatusWithType:type];
  124. UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self
  125. action:@selector(contentHeaderTap:)];
  126. [cell addGestureRecognizer:gesture];
  127. cell.userInteractionEnabled = YES;
  128. x = x + w;
  129. }
  130. }
  131. - (void)contentHeaderTap:(UIGestureRecognizer *)gesture
  132. {
  133. FLEXTableColumnHeader *header = (FLEXTableColumnHeader *)gesture.view;
  134. NSString *string = header.label.text;
  135. FLEXTableColumnHeaderSortType currentType = [self.sortStatusDict[string] integerValue];
  136. FLEXTableColumnHeaderSortType newType ;
  137. switch (currentType) {
  138. case FLEXTableColumnHeaderSortTypeNone:
  139. newType = FLEXTableColumnHeaderSortTypeAsc;
  140. break;
  141. case FLEXTableColumnHeaderSortTypeAsc:
  142. newType = FLEXTableColumnHeaderSortTypeDesc;
  143. break;
  144. case FLEXTableColumnHeaderSortTypeDesc:
  145. newType = FLEXTableColumnHeaderSortTypeAsc;
  146. break;
  147. }
  148. self.sortStatusDict = @{header.label.text : @(newType)};
  149. [header changeSortStatusWithType:newType];
  150. [self.delegate multiColumnTableView:self didTapHeaderWithText:string sortType:newType];
  151. }
  152. - (void)loadContentData
  153. {
  154. [self.contentTableView reloadData];
  155. }
  156. - (void)loadLeftViewData
  157. {
  158. [self.leftTableView reloadData];
  159. }
  160. - (UITableViewCell *)tableView:(UITableView *)tableView
  161. cellForRowAtIndexPath:(NSIndexPath *)indexPath
  162. {
  163. UIColor *backgroundColor = UIColor.whiteColor;
  164. if (indexPath.row % 2 != 0) {
  165. backgroundColor = [UIColor colorWithWhite:0.950 alpha:0.750];
  166. }
  167. if (tableView != self.leftTableView) {
  168. self.rowData = [self.dataSource contentAtRow:indexPath.row];
  169. FLEXTableContentCell *cell = [FLEXTableContentCell cellWithTableView:tableView
  170. columnNumber:[self numberOfColumns]];
  171. cell.contentView.backgroundColor = backgroundColor;
  172. cell.delegate = self;
  173. for (int i = 0 ; i < cell.labels.count; i++) {
  174. UILabel *label = cell.labels[i];
  175. label.textColor = UIColor.blackColor;
  176. NSString *content = [NSString stringWithFormat:@"%@",self.rowData[i]];
  177. if ([content isEqualToString:@"<null>"]) {
  178. label.textColor = UIColor.lightGrayColor;
  179. content = @"NULL";
  180. }
  181. label.text = content;
  182. label.backgroundColor = backgroundColor;
  183. }
  184. return cell;
  185. }
  186. else {
  187. FLEXTableLeftCell *cell = [FLEXTableLeftCell cellWithTableView:tableView];
  188. cell.contentView.backgroundColor = backgroundColor;
  189. cell.titlelabel.text = [self rowTitleForRow:indexPath.row];
  190. return cell;
  191. }
  192. }
  193. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  194. {
  195. return [self.dataSource numberOfRowsInTableView:self];
  196. }
  197. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
  198. {
  199. return [self.dataSource multiColumnTableView:self heightForContentCellInRow:indexPath.row];
  200. }
  201. - (void)scrollViewDidScroll:(UIScrollView *)scrollView
  202. {
  203. if (scrollView == self.contentScrollView) {
  204. self.headerScrollView.contentOffset = scrollView.contentOffset;
  205. }
  206. else if (scrollView == self.headerScrollView) {
  207. self.contentScrollView.contentOffset = scrollView.contentOffset;
  208. }
  209. else if (scrollView == self.leftTableView) {
  210. self.contentTableView.contentOffset = scrollView.contentOffset;
  211. }
  212. else if (scrollView == self.contentTableView) {
  213. self.leftTableView.contentOffset = scrollView.contentOffset;
  214. }
  215. }
  216. #pragma mark -
  217. #pragma mark UITableView Delegate
  218. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  219. {
  220. if (tableView == self.leftTableView) {
  221. [self.contentTableView selectRowAtIndexPath:indexPath
  222. animated:NO
  223. scrollPosition:UITableViewScrollPositionNone];
  224. }
  225. else if (tableView == self.contentTableView) {
  226. [self.leftTableView selectRowAtIndexPath:indexPath
  227. animated:NO
  228. scrollPosition:UITableViewScrollPositionNone];
  229. }
  230. }
  231. #pragma mark -
  232. #pragma mark DataSource Accessor
  233. - (NSInteger)numberOfRows
  234. {
  235. return [self.dataSource numberOfRowsInTableView:self];
  236. }
  237. - (NSInteger)numberOfColumns
  238. {
  239. return [self.dataSource numberOfColumnsInTableView:self];
  240. }
  241. - (NSString *)columnTitleForColumn:(NSInteger)column
  242. {
  243. return [self.dataSource columnNameInColumn:column];
  244. }
  245. - (NSString *)rowTitleForRow:(NSInteger)row
  246. {
  247. return [self.dataSource rowNameInRow:row];
  248. }
  249. - (NSString *)contentAtColumn:(NSInteger)column row:(NSInteger)row;
  250. {
  251. return [self.dataSource contentAtColumn:column row:row];
  252. }
  253. - (CGFloat)contentWidthForColumn:(NSInteger)column
  254. {
  255. return [self.dataSource multiColumnTableView:self widthForContentCellInColumn:column];
  256. }
  257. - (CGFloat)contentHeightForRow:(NSInteger)row
  258. {
  259. return [self.dataSource multiColumnTableView:self heightForContentCellInRow:row];
  260. }
  261. - (CGFloat)topHeaderHeight
  262. {
  263. return [self.dataSource heightForTopHeaderInTableView:self];
  264. }
  265. - (CGFloat)leftHeaderWidth
  266. {
  267. return [self.dataSource widthForLeftHeaderInTableView:self];
  268. }
  269. - (CGFloat)columnMargin
  270. {
  271. return kColumnMargin;
  272. }
  273. - (void)tableContentCell:(FLEXTableContentCell *)tableView labelDidTapWithText:(NSString *)text
  274. {
  275. [self.delegate multiColumnTableView:self didTapLabelWithText:text];
  276. }
  277. @end