JXCategoryTitleCell.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. //
  2. // JXCategoryTitleCell.m
  3. // UI系列测试
  4. //
  5. // Created by jiaxin on 2018/3/15.
  6. // Copyright © 2018年 jiaxin. All rights reserved.
  7. //
  8. #import "JXCategoryTitleCell.h"
  9. #import "JXCategoryTitleCellModel.h"
  10. #import "JXCategoryFactory.h"
  11. @interface JXCategoryTitleCell ()
  12. @property (nonatomic, strong) CALayer *titleMaskLayer;
  13. @property (nonatomic, strong) CALayer *maskTitleMaskLayer;
  14. @property (nonatomic, strong) NSLayoutConstraint *titleLabelCenterX;
  15. @property (nonatomic, strong) NSLayoutConstraint *titleLabelCenterY;
  16. @property (nonatomic, strong) NSLayoutConstraint *maskTitleLabelCenterX;
  17. @property (nonatomic, strong) NSLayoutConstraint *maskTitleLabelCenterY;
  18. @end
  19. @implementation JXCategoryTitleCell
  20. - (void)initializeViews
  21. {
  22. [super initializeViews];
  23. _titleLabel = [[UILabel alloc] init];
  24. self.titleLabel.clipsToBounds = YES;
  25. self.titleLabel.textAlignment = NSTextAlignmentCenter;
  26. self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
  27. [self.contentView addSubview:self.titleLabel];
  28. self.titleLabelCenterX = [NSLayoutConstraint constraintWithItem:self.titleLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
  29. self.titleLabelCenterY = [NSLayoutConstraint constraintWithItem:self.titleLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
  30. [NSLayoutConstraint activateConstraints:@[self.titleLabelCenterX, self.titleLabelCenterY]];
  31. _titleMaskLayer = [CALayer layer];
  32. self.titleMaskLayer.backgroundColor = [UIColor redColor].CGColor;
  33. _maskTitleLabel = [[UILabel alloc] init];
  34. self.maskTitleLabel.hidden = YES;
  35. self.maskTitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
  36. self.maskTitleLabel.textAlignment = NSTextAlignmentCenter;
  37. [self.contentView addSubview:self.maskTitleLabel];
  38. self.maskTitleLabelCenterX = [NSLayoutConstraint constraintWithItem:self.maskTitleLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
  39. self.maskTitleLabelCenterY = [NSLayoutConstraint constraintWithItem:self.maskTitleLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
  40. [NSLayoutConstraint activateConstraints:@[self.maskTitleLabelCenterX, self.maskTitleLabelCenterY]];
  41. _maskTitleMaskLayer = [CALayer layer];
  42. self.maskTitleMaskLayer.backgroundColor = [UIColor redColor].CGColor;
  43. self.maskTitleLabel.layer.mask = self.maskTitleMaskLayer;
  44. }
  45. - (void)layoutSubviews {
  46. [super layoutSubviews];
  47. //因为titleLabel是通过约束布局的,在layoutSubviews方法中,它的frame并没有确定。像子类JXCategoryNumberCell中的numberLabel需要依赖于titleLabel的frame进行布局。所以这里必须立马触发self.contentView的视图布局。
  48. [self.contentView setNeedsLayout];
  49. [self.contentView layoutIfNeeded];
  50. JXCategoryTitleCellModel *myCellModel = (JXCategoryTitleCellModel *)self.cellModel;
  51. switch (myCellModel.titleLabelAnchorPointStyle) {
  52. case JXCategoryTitleLabelAnchorPointStyleCenter:
  53. self.titleLabelCenterY.constant = 0 + myCellModel.titleLabelVerticalOffset;
  54. break;
  55. case JXCategoryTitleLabelAnchorPointStyleTop:
  56. self.titleLabelCenterY.constant = -self.titleLabel.bounds.size.height/2 - myCellModel.titleLabelVerticalOffset;
  57. break;
  58. case JXCategoryTitleLabelAnchorPointStyleBottom:
  59. self.titleLabelCenterY.constant = self.titleLabel.bounds.size.height/2 + myCellModel.titleLabelVerticalOffset;
  60. break;
  61. default:
  62. break;
  63. }
  64. }
  65. - (void)reloadData:(JXCategoryBaseCellModel *)cellModel {
  66. [super reloadData:cellModel];
  67. JXCategoryTitleCellModel *myCellModel = (JXCategoryTitleCellModel *)cellModel;
  68. self.titleLabel.numberOfLines = myCellModel.titleNumberOfLines;
  69. self.maskTitleLabel.numberOfLines = myCellModel.titleNumberOfLines;
  70. switch (myCellModel.titleLabelAnchorPointStyle) {
  71. case JXCategoryTitleLabelAnchorPointStyleCenter:
  72. self.titleLabel.layer.anchorPoint = CGPointMake(0.5, 0.5);
  73. self.maskTitleLabel.layer.anchorPoint = CGPointMake(0.5, 0.5);
  74. break;
  75. case JXCategoryTitleLabelAnchorPointStyleTop:
  76. self.titleLabel.layer.anchorPoint = CGPointMake(0.5, 0);
  77. self.maskTitleLabel.layer.anchorPoint = CGPointMake(0.5, 0);
  78. break;
  79. case JXCategoryTitleLabelAnchorPointStyleBottom:
  80. self.titleLabel.layer.anchorPoint = CGPointMake(0.5, 1);
  81. self.maskTitleLabel.layer.anchorPoint = CGPointMake(0.5, 1);
  82. break;
  83. default:
  84. break;
  85. }
  86. if (myCellModel.isTitleLabelZoomEnabled) {
  87. //先把font设置为缩放的最大值,再缩小到最小值,最后根据当前的titleLabelZoomScale值,进行缩放更新。这样就能避免transform从小到大时字体模糊
  88. UIFont *maxScaleFont = [UIFont fontWithDescriptor:myCellModel.titleFont.fontDescriptor size:myCellModel.titleFont.pointSize*myCellModel.titleLabelSelectedZoomScale];
  89. CGFloat baseScale = myCellModel.titleFont.lineHeight/maxScaleFont.lineHeight;
  90. if (myCellModel.isSelectedAnimationEnabled && [self checkCanStartSelectedAnimation:myCellModel]) {
  91. JXCategoryCellSelectedAnimationBlock block = [self preferredTitleZoomAnimationBlock:myCellModel baseScale:baseScale];
  92. [self addSelectedAnimationBlock:block];
  93. }else {
  94. self.titleLabel.font = maxScaleFont;
  95. self.maskTitleLabel.font = maxScaleFont;
  96. CGAffineTransform currentTransform = CGAffineTransformMakeScale(baseScale*myCellModel.titleLabelCurrentZoomScale, baseScale*myCellModel.titleLabelCurrentZoomScale);
  97. self.titleLabel.transform = currentTransform;
  98. self.maskTitleLabel.transform = currentTransform;
  99. }
  100. }else {
  101. if (myCellModel.isSelected) {
  102. self.titleLabel.font = myCellModel.titleSelectedFont;
  103. self.maskTitleLabel.font = myCellModel.titleSelectedFont;
  104. }else {
  105. self.titleLabel.font = myCellModel.titleFont;
  106. self.maskTitleLabel.font = myCellModel.titleFont;
  107. }
  108. }
  109. NSString *titleString = myCellModel.title ? myCellModel.title : @"";
  110. NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:titleString];
  111. if (myCellModel.isTitleLabelStrokeWidthEnabled) {
  112. if (myCellModel.isSelectedAnimationEnabled && [self checkCanStartSelectedAnimation:myCellModel]) {
  113. JXCategoryCellSelectedAnimationBlock block = [self preferredTitleStrokeWidthAnimationBlock:myCellModel attributedString:attributedString];
  114. [self addSelectedAnimationBlock:block];
  115. }else {
  116. [attributedString addAttribute:NSStrokeWidthAttributeName value:@(myCellModel.titleLabelCurrentStrokeWidth) range:NSMakeRange(0, titleString.length)];
  117. self.titleLabel.attributedText = attributedString;
  118. self.maskTitleLabel.attributedText = attributedString;
  119. }
  120. }else {
  121. self.titleLabel.attributedText = attributedString;
  122. self.maskTitleLabel.attributedText = attributedString;
  123. }
  124. if (myCellModel.isTitleLabelMaskEnabled) {
  125. self.maskTitleLabel.hidden = NO;
  126. self.titleLabel.textColor = myCellModel.titleNormalColor;
  127. self.maskTitleLabel.textColor = myCellModel.titleSelectedColor;
  128. [self.contentView setNeedsLayout];
  129. [self.contentView layoutIfNeeded];
  130. CGRect topMaskframe = myCellModel.backgroundViewMaskFrame;
  131. //将相对于cell的backgroundViewMaskFrame转换为相对于maskTitleLabel
  132. //使用self.bounds.size.width而不是self.contentView.bounds.size.width。因为某些情况下,会出现self.bounds是正确的,而self.contentView.bounds还是重用前的状态。
  133. topMaskframe.origin.y = 0;
  134. CGRect bottomMaskFrame = topMaskframe;
  135. CGFloat maskStartX = 0;
  136. if (self.maskTitleLabel.bounds.size.width >= self.bounds.size.width) {
  137. topMaskframe.origin.x -= (self.maskTitleLabel.bounds.size.width -self.bounds.size.width)/2;
  138. bottomMaskFrame.size.width = self.maskTitleLabel.bounds.size.width;
  139. maskStartX = -(self.maskTitleLabel.bounds.size.width -self.bounds.size.width)/2;
  140. }else {
  141. bottomMaskFrame.size.width = self.bounds.size.width;
  142. topMaskframe.origin.x -= (self.bounds.size.width -self.maskTitleLabel.bounds.size.width)/2;
  143. maskStartX = 0;
  144. }
  145. bottomMaskFrame.origin.x = topMaskframe.origin.x;
  146. if (topMaskframe.origin.x > maskStartX) {
  147. bottomMaskFrame.origin.x = topMaskframe.origin.x - bottomMaskFrame.size.width;
  148. }else {
  149. bottomMaskFrame.origin.x = CGRectGetMaxX(topMaskframe);
  150. }
  151. [CATransaction begin];
  152. [CATransaction setDisableActions:YES];
  153. if (topMaskframe.size.width > 0 && CGRectIntersectsRect(topMaskframe, self.maskTitleLabel.frame)) {
  154. self.titleLabel.layer.mask = self.titleMaskLayer;
  155. self.maskTitleMaskLayer.frame = topMaskframe;
  156. self.titleMaskLayer.frame = bottomMaskFrame;
  157. }else {
  158. self.maskTitleMaskLayer.frame = topMaskframe;
  159. self.titleLabel.layer.mask = nil;
  160. }
  161. [CATransaction commit];
  162. }else {
  163. self.maskTitleLabel.hidden = YES;
  164. self.titleLabel.layer.mask = nil;
  165. if (myCellModel.isSelectedAnimationEnabled && [self checkCanStartSelectedAnimation:myCellModel]) {
  166. JXCategoryCellSelectedAnimationBlock block = [self preferredTitleColorAnimationBlock:myCellModel];
  167. [self addSelectedAnimationBlock:block];
  168. }else {
  169. self.titleLabel.textColor = myCellModel.titleCurrentColor;
  170. }
  171. }
  172. [self startSelectedAnimationIfNeeded:myCellModel];
  173. }
  174. - (JXCategoryCellSelectedAnimationBlock)preferredTitleZoomAnimationBlock:(JXCategoryTitleCellModel *)cellModel baseScale:(CGFloat)baseScale {
  175. __weak typeof(self) weakSelf = self;
  176. return ^(CGFloat percent) {
  177. if (cellModel.isSelected) {
  178. //将要选中,scale从小到大插值渐变
  179. cellModel.titleLabelCurrentZoomScale = [JXCategoryFactory interpolationFrom:cellModel.titleLabelNormalZoomScale to:cellModel.titleLabelSelectedZoomScale percent:percent];
  180. }else {
  181. //将要取消选中,scale从大到小插值渐变
  182. cellModel.titleLabelCurrentZoomScale = [JXCategoryFactory interpolationFrom:cellModel.titleLabelSelectedZoomScale to:cellModel.titleLabelNormalZoomScale percent:percent];
  183. }
  184. CGAffineTransform currentTransform = CGAffineTransformMakeScale(baseScale*cellModel.titleLabelCurrentZoomScale, baseScale*cellModel.titleLabelCurrentZoomScale);
  185. weakSelf.titleLabel.transform = currentTransform;
  186. weakSelf.maskTitleLabel.transform = currentTransform;
  187. };
  188. }
  189. - (JXCategoryCellSelectedAnimationBlock)preferredTitleStrokeWidthAnimationBlock:(JXCategoryTitleCellModel *)cellModel attributedString:(NSMutableAttributedString *)attributedString {
  190. __weak typeof(self) weakSelf = self;
  191. return ^(CGFloat percent) {
  192. if (cellModel.isSelected) {
  193. //将要选中,StrokeWidth从小到大插值渐变
  194. cellModel.titleLabelCurrentStrokeWidth = [JXCategoryFactory interpolationFrom:cellModel.titleLabelNormalStrokeWidth to:cellModel.titleLabelSelectedStrokeWidth percent:percent];
  195. }else {
  196. //将要取消选中,StrokeWidth从大到小插值渐变
  197. cellModel.titleLabelCurrentStrokeWidth = [JXCategoryFactory interpolationFrom:cellModel.titleLabelSelectedStrokeWidth to:cellModel.titleLabelNormalStrokeWidth percent:percent];
  198. }
  199. [attributedString addAttribute:NSStrokeWidthAttributeName value:@(cellModel.titleLabelCurrentStrokeWidth) range:NSMakeRange(0, attributedString.string.length)];
  200. weakSelf.titleLabel.attributedText = attributedString;
  201. weakSelf.maskTitleLabel.attributedText = attributedString;
  202. };
  203. }
  204. - (JXCategoryCellSelectedAnimationBlock)preferredTitleColorAnimationBlock:(JXCategoryTitleCellModel *)cellModel {
  205. __weak typeof(self) weakSelf = self;
  206. return ^(CGFloat percent) {
  207. if (cellModel.isSelected) {
  208. //将要选中,textColor从titleNormalColor到titleSelectedColor插值渐变
  209. cellModel.titleCurrentColor = [JXCategoryFactory interpolationColorFrom:cellModel.titleNormalColor to:cellModel.titleSelectedColor percent:percent];
  210. }else {
  211. //将要取消选中,textColor从titleSelectedColor到titleNormalColor插值渐变
  212. cellModel.titleCurrentColor = [JXCategoryFactory interpolationColorFrom:cellModel.titleSelectedColor to:cellModel.titleNormalColor percent:percent];
  213. }
  214. weakSelf.titleLabel.textColor = cellModel.titleCurrentColor;
  215. };
  216. }
  217. @end