PGPickerColumnView.m 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. //
  2. // PGPickerColumnView.m
  3. // PGPickerView
  4. //
  5. // Created by piggybear on 2017/7/26.
  6. // Copyright © 2017年 piggybear. All rights reserved.
  7. //
  8. #import "PGPickerColumnView.h"
  9. #import "PGPickerColumnCell.h"
  10. #import "PGPickerTableView.h"
  11. @interface PGPickerColumnView()<UITableViewDelegate, UITableViewDataSource>
  12. @property (nonatomic) CGFloat rowHeight;
  13. @property (nonatomic, weak) UIView *upView;
  14. @property (nonatomic, weak) UIView *centerView;
  15. @property (nonatomic, weak) UIView *downView;
  16. @property (nonatomic, weak) PGPickerTableView *upTableView;
  17. @property (nonatomic, weak) PGPickerTableView *centerTableView;
  18. @property (nonatomic, weak) PGPickerTableView *downTableView;
  19. @property (nonatomic, assign) CGFloat offset;
  20. @property (nonatomic, assign) NSInteger offsetCount;
  21. @property (nonatomic) CGFloat upLinePosY;
  22. @property (nonatomic, assign) CGFloat upLineHeight;
  23. @property (nonatomic, assign) CGFloat downLineHeight;
  24. @property (nonatomic, assign) CGFloat showCount;
  25. @property (nonatomic, assign) BOOL isSubViewLayouted;
  26. @property (nonatomic, assign) CGFloat circumference;
  27. @property (nonatomic, assign) CGFloat radius;
  28. @property(nonatomic, assign) BOOL copyCycleScroll;
  29. @property (nonatomic, assign) NSInteger copyOffsetCount;
  30. @property(nonatomic, assign) BOOL isSelectedRow;
  31. @end
  32. @implementation PGPickerColumnView
  33. #define kWidth self.frame.size.width
  34. #define kHeight self.frame.size.height
  35. static NSString *const cellReuseIdentifier = @"PGPickerColumnCell";
  36. - (instancetype)initWithFrame:(CGRect)frame rowHeight:(CGFloat)rowHeight upLineHeight:(CGFloat)upLineHeight downLineHeight:(CGFloat)downLineHeight isCycleScroll:(BOOL)isCycleScroll datas:(NSArray *)datas {
  37. if (self = [super initWithFrame:frame]) {
  38. self.isCycleScroll = isCycleScroll;
  39. self.copyCycleScroll = isCycleScroll;
  40. self.rowHeight = rowHeight;
  41. self.upLineHeight = upLineHeight;
  42. self.downLineHeight = downLineHeight;
  43. self.backgroundColor = [UIColor clearColor];
  44. self.upLinePosY = (self.bounds.size.height - self.rowHeight) / 2 - self.upLineHeight;
  45. NSInteger index = self.upLinePosY / self.rowHeight;
  46. self.offsetCount = index + 1;
  47. self.offset = self.offsetCount * self.rowHeight - self.upLinePosY;
  48. if (self.offset == self.rowHeight) {
  49. self.offset = 0;
  50. self.offsetCount -= 1;
  51. }
  52. self.showCount = (frame.size.height / self.rowHeight - 1) / 2;
  53. self.circumference = self.rowHeight * self.showCount * 2 - 25;
  54. self.radius = self.circumference / M_PI * 2;
  55. self.copyOffsetCount = self.offsetCount;
  56. [self setupCycleScrollWithDatas:datas];
  57. [self setupView];
  58. }
  59. return self;
  60. }
  61. - (void)layoutSubviews {
  62. [super layoutSubviews];
  63. if (self.isSubViewLayouted) {
  64. return;
  65. }
  66. self.isSubViewLayouted = true;
  67. }
  68. - (void)setupCycleScrollWithDatas:(NSArray *)datas {
  69. if (self.frame.size.height >= self.rowHeight * datas.count) {
  70. self.isCycleScroll = false;
  71. }else {
  72. self.isCycleScroll = self.copyCycleScroll;
  73. }
  74. if (self.isCycleScroll) {
  75. self.offsetCount = 0;
  76. }else {
  77. self.offsetCount = self.copyOffsetCount;
  78. }
  79. }
  80. - (void)setupView {
  81. CGFloat upViewHeight = kHeight / 2 - self.rowHeight / 2 - self.upLineHeight;
  82. CGFloat centerViewPosY = upViewHeight + self.upLineHeight;
  83. CGFloat downViewPosY = centerViewPosY + self.rowHeight + self.downLineHeight;
  84. UIView *upView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, kWidth, upViewHeight)];
  85. upView.backgroundColor = [UIColor clearColor];
  86. upView.clipsToBounds = true;
  87. [self addSubview:upView];
  88. self.upView = upView;
  89. UIView *downView = [[UIView alloc]initWithFrame:CGRectMake(0, downViewPosY, kWidth, kHeight - downViewPosY)];
  90. downView.backgroundColor = [UIColor clearColor];
  91. downView.clipsToBounds = true;
  92. [self addSubview:downView];
  93. self.downView = downView;
  94. UIView *centerView = [[UIView alloc]initWithFrame:CGRectMake(0, centerViewPosY, kWidth, self.rowHeight)];
  95. centerView.backgroundColor = [UIColor clearColor];
  96. centerView.clipsToBounds = true;
  97. [self addSubview:centerView];
  98. self.centerView = centerView;
  99. [self setupTableView];
  100. }
  101. - (void)setupTableView {
  102. {
  103. CGRect frame = self.bounds;
  104. frame.origin.y = -self.offset;
  105. frame.size.height += self.offset;
  106. PGPickerTableView *tableView = [[PGPickerTableView alloc]initWithFrame:frame style:UITableViewStylePlain];
  107. [tableView registerClass:[PGPickerColumnCell class] forCellReuseIdentifier:cellReuseIdentifier];
  108. tableView.delegate = self;
  109. tableView.dataSource = self;
  110. tableView.showsVerticalScrollIndicator = false;
  111. tableView.showsHorizontalScrollIndicator = false;
  112. [self.upView addSubview:tableView];
  113. self.upTableView = tableView;
  114. }
  115. {
  116. CGRect frame = [self convertRect:self.upTableView.frame toView:self.centerView];
  117. PGPickerTableView *tableView = [[PGPickerTableView alloc]initWithFrame:frame style:UITableViewStylePlain];
  118. [tableView registerClass:[PGPickerColumnCell class] forCellReuseIdentifier:cellReuseIdentifier];
  119. tableView.delegate = self;
  120. tableView.dataSource = self;
  121. tableView.showsVerticalScrollIndicator = false;
  122. tableView.showsHorizontalScrollIndicator = false;
  123. [self.centerView addSubview:tableView];
  124. self.centerTableView = tableView;
  125. [self bringSubviewToFront:tableView];
  126. }
  127. {
  128. CGRect frame = [self convertRect:self.upTableView.frame toView:self.downView];
  129. PGPickerTableView *tableView = [[PGPickerTableView alloc]initWithFrame:frame style:UITableViewStylePlain];
  130. [tableView registerClass:[PGPickerColumnCell class] forCellReuseIdentifier:cellReuseIdentifier];
  131. tableView.delegate = self;
  132. tableView.dataSource = self;
  133. tableView.showsVerticalScrollIndicator = false;
  134. tableView.showsHorizontalScrollIndicator = false;
  135. [self.downView addSubview:tableView];
  136. self.downTableView = tableView;
  137. }
  138. }
  139. - (NSInteger)setupSelectedRow {
  140. NSInteger row = self.centerTableView.contentOffset.y / self.rowHeight + 0.5;
  141. if (self.isCycleScroll) {
  142. CGFloat posY = self.centerTableView.contentOffset.y + self.copyOffsetCount * self.rowHeight + self.rowHeight / 2;
  143. NSInteger count = posY / (self.datas.count * self.rowHeight);
  144. CGFloat newPosY = (self.centerTableView.contentOffset.y + self.copyOffsetCount * self.rowHeight) - (self.datas.count * self.rowHeight) * count;
  145. if (newPosY < 0) {
  146. newPosY = 0;
  147. }
  148. row = newPosY / self.rowHeight + 0.5;
  149. }
  150. return row;
  151. }
  152. - (void)setupTableViewScroll:(UITableView *)tableView animated:(BOOL)animated {
  153. CGPoint offsetPoint = CGPointMake(tableView.contentOffset.x, tableView.contentOffset.y + self.rowHeight / 2);
  154. NSIndexPath *indexPath = [tableView indexPathForRowAtPoint: offsetPoint];
  155. [tableView scrollToRowAtIndexPath: indexPath atScrollPosition: UITableViewScrollPositionTop animated:animated];
  156. }
  157. - (void)selectRow:(NSInteger)row animated:(BOOL)animated {
  158. NSInteger newRow = row;
  159. if (self.isCycleScroll) {
  160. newRow = row - self.copyOffsetCount;
  161. }
  162. if (animated) {
  163. NSIndexPath *indexPath = [NSIndexPath indexPathForRow:newRow inSection:0];
  164. [self.centerTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:true];
  165. self.selectedRow = row;
  166. self.isSelectedRow = true;
  167. }else {
  168. self.centerTableView.contentOffset = CGPointMake(0, newRow * self.rowHeight);
  169. [self scrollViewDidEndDecelerating: self.centerTableView];
  170. }
  171. }
  172. #pragma mark - UIScrollViewDelegate
  173. - (void)scrollViewDidScroll:(UITableView *)tableView {
  174. CGPoint offset = tableView.contentOffset;
  175. NSInteger rowHeight = self.rowHeight;
  176. NSInteger posY = offset.y;
  177. NSInteger value = posY % rowHeight;
  178. CGFloat itemAngle = value * ((rowHeight / self.radius) / rowHeight);
  179. if (self.isCycleScroll) {
  180. if (posY < self.rowHeight * self.datas.count * 2) {
  181. posY = posY + self.rowHeight * self.datas.count;
  182. }
  183. if (posY > self.rowHeight * self.datas.count * 3) {
  184. posY = posY - self.rowHeight * self.datas.count;
  185. }
  186. }
  187. if (self.centerTableView == tableView) {
  188. self.upTableView.contentOffset = CGPointMake(0, posY);
  189. self.downTableView.contentOffset = CGPointMake(0, posY);
  190. return;
  191. }
  192. if (tableView == self.downTableView) {
  193. self.centerTableView.contentOffset = CGPointMake(0, posY);
  194. if (!self.isHiddenWheels) {
  195. [tableView.visibleCells enumerateObjectsUsingBlock:^(__kindof PGPickerColumnCell * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  196. NSUInteger index = idx - self.showCount;
  197. NSInteger length = index * rowHeight;
  198. CGFloat angle = length / self.radius - itemAngle;
  199. CGFloat scale = cos(angle / 2);
  200. [obj transformWith:angle scale:scale];
  201. }];
  202. }
  203. return;
  204. }
  205. if (tableView == self.upTableView) {
  206. self.centerTableView.contentOffset = CGPointMake(0, posY);
  207. if (!self.isHiddenWheels) {
  208. [tableView.visibleCells enumerateObjectsUsingBlock:^(__kindof PGPickerColumnCell * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  209. NSUInteger index = self.showCount - idx;
  210. NSInteger length = index * rowHeight;
  211. CGFloat angle = length / self.radius + itemAngle;
  212. CGFloat scale = cos(angle / 2);
  213. [obj transformWith:angle scale: scale];
  214. }];
  215. }
  216. }
  217. }
  218. - (void)scrollViewDidEndDragging:(UITableView *)scrollView willDecelerate:(BOOL)decelerate{
  219. if (decelerate) return;
  220. [self scrollViewDidEndDecelerating:scrollView];
  221. }
  222. - (void)scrollViewDidEndDecelerating:(UITableView *)tableView {
  223. [self setupTableViewScroll:tableView animated:true];
  224. self.selectedRow = [self setupSelectedRow];
  225. }
  226. - (void)scrollViewDidEndScrollingAnimation:(UITableView *)tableView {
  227. if (!self.isSelectedRow) {
  228. [self setupTableViewScroll:tableView animated:false];
  229. NSUInteger row = [self setupSelectedRow];
  230. self.selectedRow = row;
  231. }
  232. if (self.isSelectedRow) {
  233. self.isSelectedRow = false;
  234. }
  235. }
  236. #pragma mark - row logic
  237. - (NSUInteger)numberOfRowsInTableView {
  238. return self.datas.count + self.offsetCount * 2;
  239. }
  240. #pragma mark - UITableViewDataSource
  241. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
  242. if (self.isCycleScroll) {
  243. return 10;
  244. }
  245. return 1;
  246. }
  247. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  248. return [self numberOfRowsInTableView];
  249. }
  250. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  251. tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  252. PGPickerColumnCell *cell = [tableView dequeueReusableCellWithIdentifier:cellReuseIdentifier];
  253. NSInteger row = indexPath.row - self.offsetCount;
  254. if (indexPath.row < self.offsetCount || row >= self.datas.count) {
  255. cell.label.attributedText = [[NSAttributedString alloc] initWithString: @""];
  256. cell.contentView.backgroundColor = [UIColor clearColor];
  257. }else {
  258. cell.label.attributedText = self.datas[row];
  259. cell.contentView.backgroundColor = self.viewBackgroundColors[row];
  260. if (!self.isHiddenWheels) {
  261. if (tableView == self.downTableView) {
  262. NSUInteger index = row - self.selectedRow;
  263. NSInteger length = index * self.rowHeight;
  264. CGFloat angle = length / self.radius;
  265. CGFloat scale = cos(angle / 2);
  266. [cell transformWith:angle scale:scale];
  267. }else if (tableView == self.upTableView) {
  268. NSUInteger index = self.selectedRow - row;
  269. NSInteger length = index * self.rowHeight;
  270. CGFloat angle = length / self.radius;
  271. CGFloat scale = cos(angle / 2);
  272. [cell transformWith:angle scale:scale];
  273. }
  274. }
  275. }
  276. if (tableView == self.centerTableView) {
  277. cell.label.textColor = self.textColorOfSelectedRow;
  278. cell.label.font = self.textFontOfSelectedRow;
  279. }else {
  280. cell.label.textColor = [self textColorOfOtherRow:row];
  281. cell.label.font = [self textFontOfOtherRow:row];
  282. }
  283. return cell;
  284. }
  285. #pragma mark - UITableViewDelegate
  286. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  287. NSInteger row = indexPath.row;
  288. if (row < self.offsetCount || row >= self.datas.count + self.offsetCount) {
  289. return;
  290. }
  291. UITableViewScrollPosition position = UITableViewScrollPositionTop;
  292. if (self.isCycleScroll) {
  293. position = UITableViewScrollPositionMiddle;
  294. }
  295. row = indexPath.row - self.offsetCount;
  296. [tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row - self.offsetCount inSection: indexPath.section] animated:true scrollPosition:position];
  297. self.selectedRow = indexPath.row - self.offsetCount;
  298. }
  299. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
  300. if ([self numberOfRowsInTableView] - 1 == indexPath.row && !self.isCycleScroll) {
  301. CGFloat tmp = self.offsetCount * self.rowHeight - self.upLinePosY;
  302. if (self.rowHeight > 44) {
  303. return fabs(tmp - self.rowHeight);
  304. }
  305. }
  306. return self.rowHeight;
  307. }
  308. - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
  309. return self.rowHeight;
  310. }
  311. #pragma mark - Setter
  312. - (void)setSelectedRow:(NSUInteger)selectedRow {
  313. _selectedRow = selectedRow;
  314. if (self.datas.count > selectedRow) {
  315. NSAttributedString *attriString = self.datas[selectedRow];
  316. self.textOfSelectedRow = attriString.string;
  317. }
  318. if (self.delegate && [self.delegate respondsToSelector:@selector(pickerColumnView:didSelectRow:)]) {
  319. [self.delegate pickerColumnView:self didSelectRow:selectedRow];
  320. }
  321. if (self.delegate && [self.delegate respondsToSelector:@selector(pickerColumnView:title:didSelectRow:)]) {
  322. [self.delegate pickerColumnView:self title:self.textOfSelectedRow didSelectRow:selectedRow];
  323. }
  324. }
  325. - (void)setDatas:(NSArray *)datas {
  326. _datas = datas;
  327. [self setupCycleScrollWithDatas:datas];
  328. [self safeReloadData];
  329. }
  330. #pragma mark - other
  331. - (UIFont *)textFontOfOtherRow:(NSInteger)row {
  332. if (self.delegate && [self.delegate respondsToSelector:@selector(pickerColumnView:textFontOfOtherRow:InComponent:)]) {
  333. return [self.delegate pickerColumnView:self textFontOfOtherRow:row InComponent:self.component];
  334. }
  335. return self.textFontOfOtherRow;
  336. }
  337. - (UIColor *)textColorOfOtherRow:(NSInteger)row {
  338. if (self.delegate && [self.delegate respondsToSelector:@selector(pickerColumnView:textColorOfOtherRow:InComponent:)]) {
  339. return [self.delegate pickerColumnView:self textColorOfOtherRow:row InComponent:self.component];
  340. }
  341. return self.textColorOfOtherRow;
  342. }
  343. - (void)safeReloadData {
  344. [self.centerTableView reloadData];
  345. [self.upTableView reloadData];
  346. [self.downTableView reloadData];
  347. NSInteger index = [self setupSelectedRow];
  348. NSAttributedString *attriString = [[NSAttributedString alloc]initWithString:@""];
  349. if (self.datas.count > index) {
  350. attriString = self.datas[index];
  351. self.textOfSelectedRow = attriString.string;
  352. }
  353. }
  354. @end