// // KBJfPayCell.m // 自定义充值选项 Cell // - 顶部金币图 + 数字 // - 底部价格 // - 选中态绿色描边 + 底部小圆勾 // #import "KBJfPayCell.h" // 用 CAShapeLayer 画底部的小圆 + 对勾 @interface KBPayCheckBadgeView : UIView @property (nonatomic, assign) BOOL checked; @end @implementation KBPayCheckBadgeView { CAShapeLayer *_circle; CAShapeLayer *_check; } - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { self.backgroundColor = UIColor.clearColor; _circle = [CAShapeLayer layer]; _circle.fillColor = [UIColor colorWithHex:KBColorValue].CGColor; // 主题绿填充 [self.layer addSublayer:_circle]; _check = [CAShapeLayer layer]; _check.strokeColor = UIColor.whiteColor.CGColor; _check.fillColor = UIColor.clearColor.CGColor; _check.lineWidth = 2.0; _check.lineCap = kCALineCapRound; _check.lineJoin = kCALineJoinRound; [self.layer addSublayer:_check]; self.hidden = YES; } return self; } - (void)layoutSubviews { [super layoutSubviews]; CGFloat s = MIN(self.bounds.size.width, self.bounds.size.height); CGRect r = CGRectMake((self.bounds.size.width - s) * 0.5, (self.bounds.size.height - s) * 0.5, s, s); UIBezierPath *circle = [UIBezierPath bezierPathWithOvalInRect:r]; _circle.path = circle.CGPath; // 对勾路径 CGPoint p1 = CGPointMake(CGRectGetMinX(r) + s*0.26, CGRectGetMinY(r) + s*0.55); CGPoint p2 = CGPointMake(CGRectGetMinX(r) + s*0.45, CGRectGetMinY(r) + s*0.74); CGPoint p3 = CGPointMake(CGRectGetMinX(r) + s*0.76, CGRectGetMinY(r) + s*0.26); UIBezierPath *check = [UIBezierPath bezierPath]; [check moveToPoint:p1]; [check addLineToPoint:p2]; [check addLineToPoint:p3]; _check.path = check.CGPath; } @end @interface KBJfPayCell () @property (nonatomic, strong) UIView *cardView; // 卡片背景(圆角) @property (nonatomic, strong) UIStackView *topStack; // 顶部水平栈:金币 + 数字(整体居中) @property (nonatomic, strong) UIImageView *iconView; // 左侧金币图 @property (nonatomic, strong) UILabel *coinLabel; // 690 @property (nonatomic, strong) UILabel *priceLabel; // $6.90 @property (nonatomic, strong) KBPayCheckBadgeView *badge; // 底部小圆勾 @property (nonatomic, strong) CAShapeLayer *borderLayer; // 选中态描边 @property (nonatomic, strong) CAShapeLayer *shadowLayer; // 选中态下方阴影 @end @implementation KBJfPayCell - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { self.contentView.backgroundColor = UIColor.clearColor; [self.contentView addSubview:self.cardView]; // 顶部金币 + 数字放入一个水平栈,保证整体居中 [self.cardView addSubview:self.topStack]; [self.topStack addArrangedSubview:self.iconView]; [self.topStack addArrangedSubview:self.coinLabel]; [self.cardView addSubview:self.priceLabel]; [self.contentView addSubview:self.badge]; [self.cardView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self.contentView); }]; [self.topStack mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.cardView).offset(16); make.centerX.equalTo(self.cardView); // 整体水平居中 }]; [self.iconView mas_makeConstraints:^(MASConstraintMaker *make) { make.width.height.mas_equalTo(18); }]; [self.priceLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(self.cardView); make.top.equalTo(self.topStack.mas_bottom).offset(8); }]; [self.badge mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(self.cardView); make.bottom.equalTo(self.cardView).offset(12); // 超出一点,营造视觉效果 make.width.height.mas_equalTo(20); }]; // 选中态描边 self.borderLayer = [CAShapeLayer layer]; self.borderLayer.strokeColor = [UIColor colorWithHex:KBColorValue].CGColor; self.borderLayer.fillColor = UIColor.clearColor.CGColor; self.borderLayer.lineWidth = 2.0; self.borderLayer.hidden = YES; [self.cardView.layer addSublayer:self.borderLayer]; // 外边框阴影(向下投影,主题绿) self.shadowLayer = [CAShapeLayer layer]; self.shadowLayer.fillColor = UIColor.clearColor.CGColor; // 不填充,仅投影 self.shadowLayer.strokeColor = UIColor.clearColor.CGColor; self.shadowLayer.shadowColor = [UIColor colorWithHex:KBColorValue].CGColor; // 0x02BEAC self.shadowLayer.shadowOpacity = 0.45; // 透明度 self.shadowLayer.shadowOffset = CGSizeMake(0, 6); // 向下 self.shadowLayer.shadowRadius = 8.0; // 模糊半径 self.shadowLayer.hidden = YES; // 放在 contentView 最底层,避免遮挡内容 [self.contentView.layer insertSublayer:self.shadowLayer atIndex:0]; } return self; } - (void)layoutSubviews { [super layoutSubviews]; CGFloat radius = 18; UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.cardView.bounds cornerRadius:radius]; self.cardView.layer.cornerRadius = radius; self.cardView.layer.masksToBounds = YES; // 内容裁剪,阴影单独用 shadowLayer 实现 self.borderLayer.path = path.CGPath; self.borderLayer.frame = self.cardView.bounds; // 阴影路径与卡片一致(放在 contentView 坐标系下) CGRect cardRectInContent = self.cardView.frame; UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRoundedRect:cardRectInContent cornerRadius:radius]; self.shadowLayer.frame = self.contentView.bounds; self.shadowLayer.shadowPath = shadowPath.CGPath; } - (void)prepareForReuse { [super prepareForReuse]; [self applySelected:NO animated:NO]; } - (void)setSelected:(BOOL)selected { [super setSelected:selected]; // 同步系统选中态到自定义 UI(保证通过 selectItemAtIndexPath 设置时也能出现描边/阴影) [self applySelected:selected animated:NO]; } - (void)configCoins:(NSString *)coins price:(NSString *)price { self.coinLabel.text = coins.length ? coins : @"690"; self.priceLabel.text = price.length ? price : @"$6.90"; } - (void)applySelected:(BOOL)selected animated:(BOOL)animated { self.borderLayer.hidden = !selected; self.badge.hidden = !selected; self.shadowLayer.hidden = !selected; // 选中时显示阴影 void (^animations)(void) = ^{ self.cardView.layer.shadowOpacity = selected ? 0.0 : 0.0; // 保持简洁 }; if (animated) { [UIView animateWithDuration:0.15 animations:animations]; } else { animations(); } } #pragma mark - Lazy UI - (UIView *)cardView { if (!_cardView) { _cardView = [UIView new]; // 淡灰渐变效果可用切图/渐变,这里简化为纯色 _cardView.backgroundColor = [UIColor colorWithWhite:0.98 alpha:1.0]; } return _cardView; } - (UIImageView *)iconView { if (!_iconView) { _iconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"shop_jb_icon"]]; _iconView.contentMode = UIViewContentModeScaleAspectFit; } return _iconView; } - (UILabel *)coinLabel { if (!_coinLabel) { _coinLabel = [UILabel new]; _coinLabel.text = @"690"; _coinLabel.font = [UIFont systemFontOfSize:20 weight:UIFontWeightBold]; _coinLabel.textColor = [UIColor colorWithHex:KBBlackValue]; } return _coinLabel; } - (UILabel *)priceLabel { if (!_priceLabel) { _priceLabel = [UILabel new]; _priceLabel.text = @"$6.90"; _priceLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightSemibold]; _priceLabel.textColor = [UIColor colorWithHex:KBColorValue]; } return _priceLabel; } - (KBPayCheckBadgeView *)badge { if (!_badge) { _badge = [[KBPayCheckBadgeView alloc] initWithFrame:CGRectZero]; _badge.userInteractionEnabled = NO; } return _badge; } - (UIStackView *)topStack { if (!_topStack) { _topStack = [[UIStackView alloc] initWithArrangedSubviews:@[]]; _topStack.axis = UILayoutConstraintAxisHorizontal; // 水平 _topStack.alignment = UIStackViewAlignmentCenter; _topStack.spacing = 6; _topStack.distribution = UIStackViewDistributionFill; } return _topStack; } @end