Files
keyboard/keyBoard/Class/Pay/V/KBJfPayCell.m
2025-11-17 14:53:23 +08:00

250 lines
9.2 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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) UIImageView *gradientImageView; // 左侧金币图
@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.gradientImageView];
// 顶部金币 + 数字放入一个水平栈,保证整体居中
[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.gradientImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.cardView);
}];
[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 colorWithHex:0xF1F1F1];
}
return _cardView;
}
- (UIImageView *)iconView {
if (!_iconView) {
_iconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"shop_jb_icon"]];
_iconView.contentMode = UIViewContentModeScaleAspectFit;
}
return _iconView;
}
- (UIImageView *)gradientImageView{
if (!_gradientImageView) {
_gradientImageView = [[UIImageView alloc] init];
UIImage *gradientImage = [UIImage kb_gradientImageWithColors:@[[UIColor colorWithHex:0xF1F1F1], [UIColor colorWithHex:0xFFFFFF]] size:CGSizeMake(108, 116) direction:KBGradientDirectionTopToBottom];
_gradientImageView.image = gradientImage;
}
return _gradientImageView;
}
- (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