JXCategoryTitleCell.m 12 KB

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