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.

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