diff --git a/keyBoard.xcodeproj/project.pbxproj b/keyBoard.xcodeproj/project.pbxproj index 7e4dd31..cbbd385 100644 --- a/keyBoard.xcodeproj/project.pbxproj +++ b/keyBoard.xcodeproj/project.pbxproj @@ -81,6 +81,8 @@ 049FB2112EC1F72F00FAB05D /* KBMyListCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 049FB2102EC1F72F00FAB05D /* KBMyListCell.m */; }; 049FB2172EC20A6600FAB05D /* BMLongPressDragCellCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 049FB2132EC20A6600FAB05D /* BMLongPressDragCellCollectionView.m */; }; 049FB21A2EC20A9E00FAB05D /* KBMyKeyBoardVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 049FB2192EC20A9E00FAB05D /* KBMyKeyBoardVC.m */; }; + 049FB2202EC30D2700FAB05D /* HomeRankDetailPopView.m in Sources */ = {isa = PBXBuildFile; fileRef = 049FB21F2EC30D2700FAB05D /* HomeRankDetailPopView.m */; }; + 049FB31D2EC21BCD00FAB05D /* KBMyKeyboardCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 049FB31C2EC21BCD00FAB05D /* KBMyKeyboardCell.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 */; }; @@ -135,7 +137,6 @@ A1B2E1012EBC7AAA00000001 /* KBTopThreeView.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2E0022EBC7AAA00000001 /* KBTopThreeView.m */; }; A1B2E1022EBC7AAA00000001 /* HomeHotCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2E0042EBC7AAA00000001 /* HomeHotCell.m */; }; ECC9EE02174D86E8D792472F /* Pods_keyBoard.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 967065BB5230E43F293B3AF9 /* Pods_keyBoard.framework */; }; - 049FB31D2EC21BCD00FAB05D /* KBMyKeyboardCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 049FB31C2EC21BCD00FAB05D /* KBMyKeyboardCell.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -284,8 +285,10 @@ 049FB2152EC20A6600FAB05D /* BMLongPressDragCellCollectionViewDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BMLongPressDragCellCollectionViewDelegate.h; sourceTree = ""; }; 049FB2182EC20A9E00FAB05D /* KBMyKeyBoardVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBMyKeyBoardVC.h; sourceTree = ""; }; 049FB2192EC20A9E00FAB05D /* KBMyKeyBoardVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBMyKeyBoardVC.m; sourceTree = ""; }; - 049FB31B2EC21BCD00FAB05D /* KBMyKeyboardCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBMyKeyboardCell.h; sourceTree = ""; }; - 049FB31C2EC21BCD00FAB05D /* KBMyKeyboardCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBMyKeyboardCell.m; sourceTree = ""; }; + 049FB21E2EC30D2700FAB05D /* HomeRankDetailPopView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HomeRankDetailPopView.h; sourceTree = ""; }; + 049FB21F2EC30D2700FAB05D /* HomeRankDetailPopView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HomeRankDetailPopView.m; sourceTree = ""; }; + 049FB31B2EC21BCD00FAB05D /* KBMyKeyboardCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBMyKeyboardCell.h; sourceTree = ""; }; + 049FB31C2EC21BCD00FAB05D /* KBMyKeyboardCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBMyKeyboardCell.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 = ""; }; @@ -779,6 +782,8 @@ 047C650C2EBC8A840035E841 /* KBPanModalView.m */, 047C65562EBCC06D0035E841 /* HomeRankCardCell.h */, 047C65572EBCC06D0035E841 /* HomeRankCardCell.m */, + 049FB21E2EC30D2700FAB05D /* HomeRankDetailPopView.h */, + 049FB21F2EC30D2700FAB05D /* HomeRankDetailPopView.m */, ); path = V; sourceTree = ""; @@ -1443,6 +1448,7 @@ 0477BDFD2EBC6A170055D639 /* HomeHotVC.m in Sources */, 048908CC2EBE373500FABA60 /* KBSearchBarView.m in Sources */, 048908CD2EBE373500FABA60 /* KBSearchSectionHeader.m in Sources */, + 049FB2202EC30D2700FAB05D /* HomeRankDetailPopView.m in Sources */, 048908CE2EBE373500FABA60 /* KBSkinCardCell.m in Sources */, 048908CF2EBE373500FABA60 /* KBTagCell.m in Sources */, 0477BEA22EBCF0000055D639 /* KBTopImageButton.m in Sources */, diff --git a/keyBoard/Assets.xcassets/Home/white_close_icon.imageset/Contents.json b/keyBoard/Assets.xcassets/Home/white_close_icon.imageset/Contents.json new file mode 100644 index 0000000..ea55260 --- /dev/null +++ b/keyBoard/Assets.xcassets/Home/white_close_icon.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "white_close_icon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "white_close_icon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Home/white_close_icon.imageset/white_close_icon@2x.png b/keyBoard/Assets.xcassets/Home/white_close_icon.imageset/white_close_icon@2x.png new file mode 100644 index 0000000..ace3ad2 Binary files /dev/null and b/keyBoard/Assets.xcassets/Home/white_close_icon.imageset/white_close_icon@2x.png differ diff --git a/keyBoard/Assets.xcassets/Home/white_close_icon.imageset/white_close_icon@3x.png b/keyBoard/Assets.xcassets/Home/white_close_icon.imageset/white_close_icon@3x.png new file mode 100644 index 0000000..eed4e0d Binary files /dev/null and b/keyBoard/Assets.xcassets/Home/white_close_icon.imageset/white_close_icon@3x.png differ diff --git a/keyBoard/Class/Home/V/HomeRankDetailPopView.h b/keyBoard/Class/Home/V/HomeRankDetailPopView.h new file mode 100644 index 0000000..b02ca6d --- /dev/null +++ b/keyBoard/Class/Home/V/HomeRankDetailPopView.h @@ -0,0 +1,29 @@ +// +// HomeRankDetailPopView.h +// keyBoard +// +// Created by Codex on 2025/11/11. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// 排行榜详情弹窗的自定义内容视图(给 LSTPopView 使用) +@interface HomeRankDetailPopView : UIView + +/// 保存按钮点击回调 +@property (nonatomic, copy, nullable) void (^saveHandler)(void); +/// 关闭按钮点击回调(底部圆形 X) +@property (nonatomic, copy, nullable) void (^closeHandler)(void); + +/// 配置数据 +- (void)configWithAvatar:(UIImage *_Nullable)avatar + title:(NSString *)title + download:(NSString *)download + desc:(NSString *)desc; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/keyBoard/Class/Home/V/HomeRankDetailPopView.m b/keyBoard/Class/Home/V/HomeRankDetailPopView.m new file mode 100644 index 0000000..129ceb7 --- /dev/null +++ b/keyBoard/Class/Home/V/HomeRankDetailPopView.m @@ -0,0 +1,241 @@ +// +// HomeRankDetailPopView.m +// keyBoard +// +// Created by Codex on 2025/11/11. +// + +@import UIKit; +#import "HomeRankDetailPopView.h" +#import + +@interface HomeRankDetailPopView () + +// 容器卡片(白底圆角) +@property (nonatomic, strong) UIView *cardView; +// 头像(圆形,带白色描边) +@property (nonatomic, strong) UIImageView *avatarView; +// 标题 +@property (nonatomic, strong) UILabel *titleLabel; +// 下载人数文案(绿色) +@property (nonatomic, strong) UILabel *downloadLabel; +// 内容气泡容器 +@property (nonatomic, strong) UIView *bubbleView; +// 内容文本 +@property (nonatomic, strong) UILabel *descLabel; +// 保存按钮 +@property (nonatomic, strong) UIButton *saveButton; +// 底部关闭按钮(圆形 X) +@property (nonatomic, strong) UIButton *closeButton; + +@end + +@implementation HomeRankDetailPopView + +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + self.backgroundColor = UIColor.clearColor; // 由 LSTPopView 提供半透明背景 + [self buildUI]; + [self makeConstraints]; + } + return self; +} + +#pragma mark - Public + +- (void)configWithAvatar:(UIImage *)avatar + title:(NSString *)title + download:(NSString *)download + desc:(NSString *)desc { + self.avatarView.image = avatar ?: [self placeholderAvatar]; + self.titleLabel.text = title ?: @""; + self.downloadLabel.text = download ?: @""; + self.descLabel.text = desc ?: @""; +} + +#pragma mark - Build UI + +- (void)buildUI { + // 添加顺序:先卡片,再头像(悬浮于卡片上方),最后底部关闭 + [self addSubview:self.cardView]; + [self addSubview:self.avatarView]; + [self addSubview:self.closeButton]; + + [self.cardView addSubview:self.titleLabel]; + [self.cardView addSubview:self.downloadLabel]; + [self.cardView addSubview:self.bubbleView]; + [self.bubbleView addSubview:self.descLabel]; + [self.cardView addSubview:self.saveButton]; +} + +- (void)makeConstraints { + // 说明:将卡片上下各预留出 48 的间距,顶部用于头像悬浮展示,底部用于放置关闭按钮 + [self.cardView mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.right.equalTo(self); + make.top.equalTo(self).offset(KBFit(56)); + make.height.mas_equalTo(KBFit(358)); +// make.bottom.equalTo(self).offset(-72); + }]; + + [self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerX.equalTo(self.cardView); + make.centerY.equalTo(self.cardView.mas_top); // 一半悬浮在卡片外 + make.width.height.mas_equalTo(112); + }]; + + [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.avatarView.mas_bottom).offset(KBFit(13)); + make.centerX.equalTo(self.cardView); + make.height.mas_equalTo(KBFit(23)); + }]; + + [self.downloadLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.titleLabel.mas_bottom).offset(KBFit(7)); + make.centerX.equalTo(self.cardView); + make.height.mas_equalTo(KBFit(23)); + }]; + + [self.bubbleView mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.downloadLabel.mas_bottom).offset(KBFit(16)); + make.left.equalTo(self.cardView).offset(20); + make.right.equalTo(self.cardView).offset(-20); + make.height.mas_equalTo(KBFit(125)); + }]; + + [self.descLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.equalTo(self.bubbleView).insets(UIEdgeInsetsMake(12, 12, 12, 12)); + }]; + + [self.saveButton mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.equalTo(self.cardView).offset(20); + make.right.equalTo(self.cardView).offset(-20); + make.bottom.equalTo(self.cardView).offset(-KBFit(26)); + make.height.mas_equalTo(KBFit(48)); + }]; + + [self.closeButton mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.cardView.mas_bottom).offset(16); + make.centerX.equalTo(self.cardView); + make.width.height.mas_equalTo(36); + }]; +} + +#pragma mark - Actions + +- (void)onTapSave { + if (self.saveHandler) self.saveHandler(); +} + +- (void)onTapClose { + if (self.closeHandler) self.closeHandler(); +} + +#pragma mark - Helpers + +- (UIImage *)placeholderAvatar { + // 简单生成圆形纯色头像占位 + CGSize size = CGSizeMake(96, 96); + UIGraphicsBeginImageContextWithOptions(size, NO, 0); + CGContextRef ctx = UIGraphicsGetCurrentContext(); + [[UIColor colorWithRed:0.93 green:0.96 blue:0.99 alpha:1.0] setFill]; + CGContextFillEllipseInRect(ctx, CGRectMake(0, 0, size.width, size.height)); + UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return img; +} + +#pragma mark - Lazy UI + +- (UIView *)cardView { + if (!_cardView) { + _cardView = [UIView new]; + _cardView.backgroundColor = UIColor.whiteColor; + _cardView.layer.cornerRadius = 22.0; + _cardView.layer.masksToBounds = YES; + } + return _cardView; +} + +- (UIImageView *)avatarView { + if (!_avatarView) { + _avatarView = [[UIImageView alloc] init]; + _avatarView.contentMode = UIViewContentModeScaleAspectFill; + _avatarView.layer.cornerRadius = 56.0; + _avatarView.layer.masksToBounds = YES; + // 外环 + _avatarView.layer.borderColor = UIColor.whiteColor.CGColor; + _avatarView.layer.borderWidth = 4.0; + _avatarView.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.0]; + } + return _avatarView; +} + +- (UILabel *)titleLabel { + if (!_titleLabel) { + _titleLabel = [UILabel new]; + _titleLabel.textColor = [UIColor colorWithHex:KBBlackValue]; + _titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold]; + _titleLabel.textAlignment = NSTextAlignmentCenter; + _titleLabel.text = @"High EQ"; // 默认文案 + } + return _titleLabel; +} + +- (UILabel *)downloadLabel { + if (!_downloadLabel) { + _downloadLabel = [UILabel new]; + _downloadLabel.textColor = [UIColor colorWithHex:KBColorValue]; + _downloadLabel.backgroundColor = [UIColor colorWithHex:0xEDFFFD]; + _downloadLabel.font = [UIFont systemFontOfSize:13 weight:UIFontWeightMedium]; + _downloadLabel.textAlignment = NSTextAlignmentCenter; + _downloadLabel.text = @"Download: 1 Million"; + } + return _downloadLabel; +} + +- (UIView *)bubbleView { + if (!_bubbleView) { + _bubbleView = [UIView new]; + _bubbleView.backgroundColor = [UIColor colorWithHex:0xF8F8F8]; + _bubbleView.layer.cornerRadius = 9.0; + _bubbleView.layer.masksToBounds = YES; + } + return _bubbleView; +} + +- (UILabel *)descLabel { + if (!_descLabel) { + _descLabel = [UILabel new]; + _descLabel.textColor = [UIColor colorWithHex:KBBlackValue]; + _descLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightSemibold]; + _descLabel.numberOfLines = 0; + _descLabel.text = @"Be Neither Too Close\nNor Too Distant"; + } + return _descLabel; +} + +- (UIButton *)saveButton { + if (!_saveButton) { + _saveButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [_saveButton setTitle:@"Save" forState:UIControlStateNormal]; + [_saveButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal]; + _saveButton.titleLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightSemibold]; + _saveButton.backgroundColor = [UIColor colorWithRed:0.02 green:0.75 blue:0.67 alpha:1.0]; + _saveButton.layer.cornerRadius = 24.0; + _saveButton.layer.masksToBounds = YES; + [_saveButton addTarget:self action:@selector(onTapSave) forControlEvents:UIControlEventTouchUpInside]; + } + return _saveButton; +} + +- (UIButton *)closeButton { + if (!_closeButton) { + _closeButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [_closeButton setImage:[UIImage imageNamed:@"white_close_icon"] forState:UIControlStateNormal]; + [_closeButton addTarget:self action:@selector(onTapClose) forControlEvents:UIControlEventTouchUpInside]; + } + return _closeButton; +} + +@end + diff --git a/keyBoard/Class/Home/VC/HomeRankContentVC.m b/keyBoard/Class/Home/VC/HomeRankContentVC.m index 2f6687e..6ae6924 100644 --- a/keyBoard/Class/Home/VC/HomeRankContentVC.m +++ b/keyBoard/Class/Home/VC/HomeRankContentVC.m @@ -7,6 +7,8 @@ @import UIKit; #import "HomeRankContentVC.h" +#import "HomeRankDetailPopView.h" // 自定义弹窗内容视图 +#import "LSTPopView.h" // LSTPopView 弹窗框架 // 自定义卡片 Cell #import "HomeRankCardCell.h" @@ -92,16 +94,44 @@ #pragma mark - UICollectionViewDelegateFlowLayout - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { - CGFloat totalHInset = 16 + 16; // section i/l/r - CGFloat spacing = 16; // interitem spacing - CGFloat w = collectionView.bounds.size.width - totalHInset; // not including section insets yet - // Two columns → each width = (width - spacing - lr insets)/2 + CGFloat totalHInset = 16 + 16; + CGFloat spacing = 16; + CGFloat w = collectionView.bounds.size.width - totalHInset; CGFloat cellWidth = (w - spacing) / 2.0; // 固定高度,接近示意图比例 CGFloat cellHeight = 234.0; return CGSizeMake(floor(cellWidth), cellHeight); } +- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{ + // 点击卡片 -> 展示弹窗 + NSDictionary *d = self.dataSource[indexPath.item]; + + // 自定义内容视图(尺寸可按需调节) + CGFloat width = MIN(KB_SCREEN_WIDTH - 76, 420); + HomeRankDetailPopView *content = [[HomeRankDetailPopView alloc] initWithFrame:CGRectMake(0, 0, width, 460)]; + NSString *title = d[@"title"] ?: @"High EQ"; + NSString *people = d[@"people"] ?: @"Download: 1 Million"; + NSString *desc = @"Be Neither Too Close\nNor Too Distant"; // 示例文案 + [content configWithAvatar:nil title:title download:people desc:desc]; + + // 创建并弹出 + LSTPopView *pop = [LSTPopView initWithCustomView:content + parentView:nil + popStyle:LSTPopStyleScale + dismissStyle:LSTDismissStyleScale]; + pop.hemStyle = LSTHemStyleCenter; // 居中 + pop.bgColor = [[UIColor blackColor] colorWithAlphaComponent:0.4]; + pop.isClickBgDismiss = YES; // 点击背景关闭 + pop.cornerRadius = 0; // 自定义 view 自处理圆角 + + __weak typeof(pop) weakPop = pop; + content.saveHandler = ^{ [weakPop dismiss]; }; + content.closeHandler = ^{ [weakPop dismiss]; }; + + [pop pop]; +} + #pragma mark - JXCategoryListContentViewDelegate - (UIView *)listView { diff --git a/keyBoard/KeyBoardPrefixHeader.pch b/keyBoard/KeyBoardPrefixHeader.pch index c6406a1..a9d78d5 100644 --- a/keyBoard/KeyBoardPrefixHeader.pch +++ b/keyBoard/KeyBoardPrefixHeader.pch @@ -57,8 +57,10 @@ #define KB_TABBAR_BASE_HEIGHT 49.0 #define COLOR_WITH_RGB(R,G,B,A) [UIColor colorWithRed:R green:G blue:B alpha:A] - +/// 主题色 绿色 #define KBColorValue 0x02BEAC +/// 通用黑色 +#define KBBlackValue 0x1B1F1A // 当前 KeyWindow(iOS 13 场景化兼容) static inline UIWindow *KB_KeyWindow(void) {