250 lines
9.2 KiB
Objective-C
250 lines
9.2 KiB
Objective-C
//
|
||
// 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
|