JXCategoryTitleCell.m 13 KB

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