From 97316c798922692a2c4f664df2f779db12b04809 Mon Sep 17 00:00:00 2001 From: CodeST <694468528@qq.com> Date: Mon, 10 Nov 2025 15:29:21 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=BA=95=E9=83=A8view?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- keyBoard.xcodeproj/project.pbxproj | 6 + .../Class/Base/V/UIScrollView+KBEmptyView.h | 1 - keyBoard/Class/Me/V/KBSkinBottomActionView.h | 37 +++++ keyBoard/Class/Me/V/KBSkinBottomActionView.m | 153 ++++++++++++++++++ keyBoard/Class/Me/VC/KBSkinDetailVC.m | 46 +++++- keyBoard/Class/Me/VC/MyVC.m | 6 +- 6 files changed, 243 insertions(+), 6 deletions(-) create mode 100644 keyBoard/Class/Me/V/KBSkinBottomActionView.h create mode 100644 keyBoard/Class/Me/V/KBSkinBottomActionView.m diff --git a/keyBoard.xcodeproj/project.pbxproj b/keyBoard.xcodeproj/project.pbxproj index 3a4de1b..997a0c0 100644 --- a/keyBoard.xcodeproj/project.pbxproj +++ b/keyBoard.xcodeproj/project.pbxproj @@ -75,6 +75,7 @@ 048909F72EC0AAAA00FABA60 /* KBCategoryTitleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 048909F32EC0AAAA00FABA60 /* KBCategoryTitleView.m */; }; 04890A042EC0BBBB00FABA60 /* KBCategoryTitleImageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04890A012EC0BBBB00FABA60 /* KBCategoryTitleImageCell.m */; }; 04890A052EC0BBBB00FABA60 /* KBCategoryTitleImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04890A032EC0BBBB00FABA60 /* KBCategoryTitleImageView.m */; }; + 049FB20B2EC1C13800FAB05D /* KBSkinBottomActionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 049FB20A2EC1C13800FAB05D /* KBSkinBottomActionView.m */; }; 04A9FE0F2EB481100020DB6D /* KBHUD.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC97082EB31B14007BD342 /* KBHUD.m */; }; 04A9FE132EB4D0D20020DB6D /* KBFullAccessManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 04A9FE112EB4D0D20020DB6D /* KBFullAccessManager.m */; }; 04A9FE162EB873C80020DB6D /* UIViewController+Extension.m in Sources */ = {isa = PBXBuildFile; fileRef = 04A9FE152EB873C80020DB6D /* UIViewController+Extension.m */; }; @@ -263,6 +264,8 @@ 04890A012EC0BBBB00FABA60 /* KBCategoryTitleImageCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBCategoryTitleImageCell.m; sourceTree = ""; }; 04890A022EC0BBBB00FABA60 /* KBCategoryTitleImageView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBCategoryTitleImageView.h; sourceTree = ""; }; 04890A032EC0BBBB00FABA60 /* KBCategoryTitleImageView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBCategoryTitleImageView.m; sourceTree = ""; }; + 049FB2092EC1C13800FAB05D /* KBSkinBottomActionView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinBottomActionView.h; sourceTree = ""; }; + 049FB20A2EC1C13800FAB05D /* KBSkinBottomActionView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinBottomActionView.m; sourceTree = ""; }; 04A9A67D2EB9E1690023B8F4 /* KBResponderUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBResponderUtils.h; sourceTree = ""; }; 04A9FE102EB4D0D20020DB6D /* KBFullAccessManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBFullAccessManager.h; sourceTree = ""; }; 04A9FE112EB4D0D20020DB6D /* KBFullAccessManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBFullAccessManager.m; sourceTree = ""; }; @@ -823,6 +826,8 @@ 048908EB2EBF849300FABA60 /* KBSkinTagsContainerCell.m */, 048908ED2EBF861800FABA60 /* KBSkinSectionTitleCell.h */, 048908EE2EBF861800FABA60 /* KBSkinSectionTitleCell.m */, + 049FB2092EC1C13800FAB05D /* KBSkinBottomActionView.h */, + 049FB20A2EC1C13800FAB05D /* KBSkinBottomActionView.m */, ); path = V; sourceTree = ""; @@ -1352,6 +1357,7 @@ 047C65102EBCA8DD0035E841 /* HomeRankContentVC.m in Sources */, 047C655C2EBCD0F80035E841 /* UIView+KBShadow.m in Sources */, 048908C32EBE32B800FABA60 /* KBSearchVC.m in Sources */, + 049FB20B2EC1C13800FAB05D /* KBSkinBottomActionView.m in Sources */, 047C655E2EBCD5B20035E841 /* UIImage+KBColor.m in Sources */, 04FC95DD2EB202A3007BD342 /* KBGuideVC.m in Sources */, 04FC95E52EB220B5007BD342 /* UIColor+Extension.m in Sources */, diff --git a/keyBoard/Class/Base/V/UIScrollView+KBEmptyView.h b/keyBoard/Class/Base/V/UIScrollView+KBEmptyView.h index c1b15ae..cb5c419 100644 --- a/keyBoard/Class/Base/V/UIScrollView+KBEmptyView.h +++ b/keyBoard/Class/Base/V/UIScrollView+KBEmptyView.h @@ -3,7 +3,6 @@ // keyBoard // // 统一封装基于 LYEmptyView 的空态视图挂载方法,适用于 UITableView/UICollectionView。 -// 注意:仅在对应页面已通过 CocoaPods 集成 LYEmptyView 时生效。 // #import diff --git a/keyBoard/Class/Me/V/KBSkinBottomActionView.h b/keyBoard/Class/Me/V/KBSkinBottomActionView.h new file mode 100644 index 0000000..d80f4c9 --- /dev/null +++ b/keyBoard/Class/Me/V/KBSkinBottomActionView.h @@ -0,0 +1,37 @@ +// +// KBSkinBottomActionView.h +// keyBoard +// +// 底部操作条(圆角胶囊样式),支持点击。用于皮肤详情页下载/购买。 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// 底部操作条(45 高,左右圆角)。 +/// 结构: [ Title [coinIcon] [price] ] 居中对齐 +@interface KBSkinBottomActionView : UIControl + +/// 标题,例如:@"Download" +@property (nonatomic, copy) NSString *titleText; + +/// 价格/金币数,例如:@"20" +@property (nonatomic, copy) NSString *priceText; + +/// 图标(可选),例如金币图 +@property (nonatomic, strong, nullable) UIImage *iconImage; + +/// 点击回调(也可直接 addTarget 使用) +@property (nonatomic, copy, nullable) void (^tapHandler)(void); + +/// 配置便捷方法 +- (void)configWithTitle:(nullable NSString *)title price:(nullable NSString *)price icon:(nullable UIImage *)icon; + +/// 建议固定高度 ++ (CGFloat)preferredHeight; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/keyBoard/Class/Me/V/KBSkinBottomActionView.m b/keyBoard/Class/Me/V/KBSkinBottomActionView.m new file mode 100644 index 0000000..0ccba7a --- /dev/null +++ b/keyBoard/Class/Me/V/KBSkinBottomActionView.m @@ -0,0 +1,153 @@ +// +// KBSkinBottomActionView.m +// keyBoard +// +// + +#import "KBSkinBottomActionView.h" + +@interface KBSkinBottomActionView () +@property (nonatomic, strong) UIView *contentView; // 内部容器,使三项整体居中 +@property (nonatomic, strong) UILabel *titleLabel; // 左侧标题 +@property (nonatomic, strong) UIImageView *coinImageView; // 中间图标(可选) +@property (nonatomic, strong) UILabel *priceLabel; // 右侧价格 +@end + +@implementation KBSkinBottomActionView + ++ (CGFloat)preferredHeight { return 45.0; } + +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + self.backgroundColor = [UIColor colorWithHex:0x02BEAC]; + self.layer.masksToBounds = YES; // 圆角生效 + + // 高亮态轻微透明,突出点击感 + [self addTarget:self action:@selector(onTouchDown) forControlEvents:UIControlEventTouchDown]; + [self addTarget:self action:@selector(onTouchUp) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside | UIControlEventTouchCancel]; + + // 内部容器,承载三个元素,容器整体水平/垂直居中 + [self addSubview:self.contentView]; + [self.contentView mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerX.equalTo(self); + make.centerY.equalTo(self); + // 防止文本过长时超出左右边界 + make.left.greaterThanOrEqualTo(self).offset(16); + make.right.lessThanOrEqualTo(self).offset(-16); + }]; + + // 三个元素放进容器,左右顺序:Title - Icon - Price + [self.contentView addSubview:self.titleLabel]; + [self.contentView addSubview:self.coinImageView]; + [self.contentView addSubview:self.priceLabel]; + + [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.equalTo(self.contentView); + make.centerY.equalTo(self.contentView); + }]; + [self.coinImageView mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.equalTo(self.titleLabel.mas_right).offset(8); + make.centerY.equalTo(self.contentView); + make.width.height.mas_equalTo(18); + }]; + [self.priceLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.equalTo(self.coinImageView.mas_right).offset(6); + make.right.equalTo(self.contentView); + make.centerY.equalTo(self.contentView); + }]; + + // 默认文案 + self.titleText = @"Download"; + self.priceText = @"20"; + UIImage *img = [UIImage systemImageNamed:@"circle.fill"]; + self.iconImage = img; // 若项目没有金币图标,用系统占位(黄色) + self.coinImageView.tintColor = [UIColor colorWithRed:1.0 green:0.85 blue:0.2 alpha:1.0]; + // 点击回调(可选) + [self addTarget:self action:@selector(handleTap) forControlEvents:UIControlEventTouchUpInside]; + } + return self; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + // 圆角随高度自适应 + self.layer.cornerRadius = CGRectGetHeight(self.bounds) * 0.5; +} + +#pragma mark - Public + +- (void)configWithTitle:(nullable NSString *)title price:(nullable NSString *)price icon:(nullable UIImage *)icon { + if (title.length) self.titleText = title; + if (price.length) self.priceText = price; + if (icon) self.iconImage = icon; +} + +#pragma mark - Actions + +- (void)onTouchDown { self.alpha = 0.85; } +- (void)onTouchUp { self.alpha = 1.0; } + +- (void)handleTap { + if (self.tapHandler) { self.tapHandler(); } +} + +#pragma mark - Setters + +- (void)setTitleText:(NSString *)titleText { + _titleText = [titleText copy]; + self.titleLabel.text = _titleText; +} + +- (void)setPriceText:(NSString *)priceText { + _priceText = [priceText copy]; + self.priceLabel.text = _priceText; +} + +- (void)setIconImage:(UIImage *)iconImage { + _iconImage = iconImage; + self.coinImageView.image = _iconImage; +} + +#pragma mark - Lazy + +- (UILabel *)titleLabel { + if (!_titleLabel) { + _titleLabel = [[UILabel alloc] init]; + _titleLabel.font = [UIFont systemFontOfSize:17 weight:UIFontWeightSemibold]; + _titleLabel.textColor = [UIColor whiteColor]; + _titleLabel.textAlignment = NSTextAlignmentCenter; + } + return _titleLabel; +} + +- (UIView *)contentView { + if (!_contentView) { + _contentView = [[UIView alloc] init]; + _contentView.backgroundColor = [UIColor clearColor]; + // 让容器尽量包裹内容 + [_contentView setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal]; + [_contentView setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal]; + } + return _contentView; +} + +- (UIImageView *)coinImageView { + if (!_coinImageView) { + _coinImageView = [[UIImageView alloc] init]; + _coinImageView.contentMode = UIViewContentModeScaleAspectFit; + _coinImageView.clipsToBounds = YES; + } + return _coinImageView; +} + +- (UILabel *)priceLabel { + if (!_priceLabel) { + _priceLabel = [[UILabel alloc] init]; + _priceLabel.font = [UIFont systemFontOfSize:17 weight:UIFontWeightSemibold]; + _priceLabel.textColor = [UIColor whiteColor]; + _priceLabel.textAlignment = NSTextAlignmentCenter; + } + return _priceLabel; +} + +@end diff --git a/keyBoard/Class/Me/VC/KBSkinDetailVC.m b/keyBoard/Class/Me/VC/KBSkinDetailVC.m index 10e002b..fbc239c 100644 --- a/keyBoard/Class/Me/VC/KBSkinDetailVC.m +++ b/keyBoard/Class/Me/VC/KBSkinDetailVC.m @@ -8,8 +8,9 @@ #import "UICollectionViewLeftAlignedLayout.h" #import "KBSkinDetailHeaderCell.h" #import "KBSkinTagsContainerCell.h" -#import "KBSkinCardCell.h" // 已有的 皮肤卡片 cell(用作 2 列网格) +#import "KBSkinCardCell.h" #import "KBSkinSectionTitleCell.h" +#import "KBSkinBottomActionView.h" static NSString * const kHeaderCellId = @"kHeaderCellId"; static NSString * const kTagsContainerCellId = @"kTagsContainerCellId"; @@ -26,6 +27,7 @@ typedef NS_ENUM(NSInteger, KBSkinDetailSection) { @interface KBSkinDetailVC () @property (nonatomic, strong) UICollectionView *collectionView; // 主列表 +@property (nonatomic, strong) KBSkinBottomActionView *bottomBar; // 底部操作条 @property (nonatomic, copy) NSArray *tags; // 标签数据 @property (nonatomic, copy) NSArray *gridData; // 底部网格数据 @end @@ -44,9 +46,26 @@ typedef NS_ENUM(NSInteger, KBSkinDetailSection) { @{ @"title": @"Dopamine" }, @{ @"title": @"Dopamine" }, ]; + // 1. 列表 [self.view addSubview:self.collectionView]; + + // 2. 底部操作条(左右 15,高 45,可点击) + [self.view addSubview:self.bottomBar]; + [self.bottomBar mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.equalTo(self.view).offset(15); + make.right.equalTo(self.view).offset(-15); + make.height.mas_equalTo([KBSkinBottomActionView preferredHeight]); + if (@available(iOS 11.0, *)) { + make.bottom.equalTo(self.view.mas_safeAreaLayoutGuideBottom); + } else { + make.bottom.equalTo(self.view); + } + }]; + + // 列表底部距离操作条顶部 10 [self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) { - make.edges.equalTo(self.view); + make.top.left.right.equalTo(self.view); + make.bottom.equalTo(self.bottomBar.mas_top).offset(-10); }]; } @@ -156,4 +175,27 @@ typedef NS_ENUM(NSInteger, KBSkinDetailSection) { return _collectionView; } +#pragma mark - Lazy (Bottom Bar) + +- (KBSkinBottomActionView *)bottomBar { + if (!_bottomBar) { + _bottomBar = [[KBSkinBottomActionView alloc] init]; + // 中文注释:配置文案与图标,可根据业务传入金币图/价格 + [_bottomBar configWithTitle:@"Download" price:@"20" icon:nil]; + __weak typeof(self) weakSelf = self; + _bottomBar.tapHandler = ^{ + // 示例:点击下载/购买 +// [KBHUD showText:@"点击了下载"]; + // TODO: 在此处触发下载/购买逻辑 + [weakSelf handleDownloadAction]; + }; + } + return _bottomBar; +} + +#pragma mark - Actions +- (void)handleDownloadAction { + // 预留:下载/购买动作 +} + @end diff --git a/keyBoard/Class/Me/VC/MyVC.m b/keyBoard/Class/Me/VC/MyVC.m index d977520..a422724 100644 --- a/keyBoard/Class/Me/VC/MyVC.m +++ b/keyBoard/Class/Me/VC/MyVC.m @@ -29,10 +29,10 @@ } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ - MySkinVC *vc = [[MySkinVC alloc] init]; -// KBSkinDetailVC *vc = [[KBSkinDetailVC alloc] init]; +// MySkinVC *vc = [[MySkinVC alloc] init]; + KBSkinDetailVC *vc = [[KBSkinDetailVC alloc] init]; - [self.navigationController pushViewController:vc animated:true]; +// [self.navigationController pushViewController:vc animated:true]; } /*