JXCategoryTitleCell.m 13 KB

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