PGPickerColumnView.m 15 KB

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