1
This commit is contained in:
@@ -45,6 +45,10 @@
|
||||
#define API_CHARACTER_DEL_USER_CHARACTER @"/character/delUserCharacter" // 删除用户人设
|
||||
#define API_CHARACTER_ADD_USER_CHARACTER @"/character/addUserCharacter" // 添加用户人设(假定路径,如有不同请按后端实际修改)
|
||||
|
||||
/// Theme shop
|
||||
#define API_THEME_LIST_ALL_STYLES @"/themes/listAllStyles" // 查询所有主题风格
|
||||
#define API_THEME_LIST_BY_STYLE @"/themes/listByStyle" // 按风格查询主题列表
|
||||
|
||||
/// pay
|
||||
#define API_VALIDATE_RECEIPT @"/api/apple/validate-receipt" // 排行榜标签列表
|
||||
|
||||
|
||||
@@ -116,6 +116,9 @@
|
||||
0498BD8F2EE6A3BD006CC1D5 /* KBMyMainModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD8E2EE6A3BD006CC1D5 /* KBMyMainModel.m */; };
|
||||
0498BD902EE6A3BD006CC1D5 /* KBMyMainModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD8E2EE6A3BD006CC1D5 /* KBMyMainModel.m */; };
|
||||
0498BDDA2EE7ECEA006CC1D5 /* WJXEventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BDD82EE7ECEA006CC1D5 /* WJXEventSource.m */; };
|
||||
0498BDDE2EE81508006CC1D5 /* KBShopVM.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BDDD2EE81508006CC1D5 /* KBShopVM.m */; };
|
||||
0498BDE12EEA87C9006CC1D5 /* KBShopStyleModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BDE02EEA87C8006CC1D5 /* KBShopStyleModel.m */; };
|
||||
0498BDE42EEA885D006CC1D5 /* KBShopThemeModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BDE32EEA885D006CC1D5 /* KBShopThemeModel.m */; };
|
||||
049FB20B2EC1C13800FAB05D /* KBSkinBottomActionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 049FB20A2EC1C13800FAB05D /* KBSkinBottomActionView.m */; };
|
||||
049FB20E2EC1CD2800FAB05D /* KBAlert.m in Sources */ = {isa = PBXBuildFile; fileRef = 049FB20D2EC1CD2800FAB05D /* KBAlert.m */; };
|
||||
049FB2112EC1F72F00FAB05D /* KBMyListCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 049FB2102EC1F72F00FAB05D /* KBMyListCell.m */; };
|
||||
@@ -408,6 +411,12 @@
|
||||
0498BD8E2EE6A3BD006CC1D5 /* KBMyMainModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBMyMainModel.m; sourceTree = "<group>"; };
|
||||
0498BDD72EE7ECEA006CC1D5 /* WJXEventSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WJXEventSource.h; sourceTree = "<group>"; };
|
||||
0498BDD82EE7ECEA006CC1D5 /* WJXEventSource.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WJXEventSource.m; sourceTree = "<group>"; };
|
||||
0498BDDC2EE81508006CC1D5 /* KBShopVM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBShopVM.h; sourceTree = "<group>"; };
|
||||
0498BDDD2EE81508006CC1D5 /* KBShopVM.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBShopVM.m; sourceTree = "<group>"; };
|
||||
0498BDDF2EEA87C8006CC1D5 /* KBShopStyleModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBShopStyleModel.h; sourceTree = "<group>"; };
|
||||
0498BDE02EEA87C8006CC1D5 /* KBShopStyleModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBShopStyleModel.m; sourceTree = "<group>"; };
|
||||
0498BDE22EEA885D006CC1D5 /* KBShopThemeModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBShopThemeModel.h; sourceTree = "<group>"; };
|
||||
0498BDE32EEA885D006CC1D5 /* KBShopThemeModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBShopThemeModel.m; sourceTree = "<group>"; };
|
||||
049FB2092EC1C13800FAB05D /* KBSkinBottomActionView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinBottomActionView.h; sourceTree = "<group>"; };
|
||||
049FB20A2EC1C13800FAB05D /* KBSkinBottomActionView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinBottomActionView.m; sourceTree = "<group>"; };
|
||||
049FB20C2EC1CD2800FAB05D /* KBAlert.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAlert.h; sourceTree = "<group>"; };
|
||||
@@ -765,6 +774,10 @@
|
||||
047C654A2EBCBA880035E841 /* M */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0498BDDF2EEA87C8006CC1D5 /* KBShopStyleModel.h */,
|
||||
0498BDE02EEA87C8006CC1D5 /* KBShopStyleModel.m */,
|
||||
0498BDE22EEA885D006CC1D5 /* KBShopThemeModel.h */,
|
||||
0498BDE32EEA885D006CC1D5 /* KBShopThemeModel.m */,
|
||||
);
|
||||
path = M;
|
||||
sourceTree = "<group>";
|
||||
@@ -800,6 +813,7 @@
|
||||
047C654D2EBCBA880035E841 /* Shop */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0498BDDB2EE814E3006CC1D5 /* VM */,
|
||||
047C654A2EBCBA880035E841 /* M */,
|
||||
047C654B2EBCBA880035E841 /* V */,
|
||||
047C654C2EBCBA880035E841 /* VC */,
|
||||
@@ -921,6 +935,15 @@
|
||||
path = WJXEventSource;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0498BDDB2EE814E3006CC1D5 /* VM */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0498BDDC2EE81508006CC1D5 /* KBShopVM.h */,
|
||||
0498BDDD2EE81508006CC1D5 /* KBShopVM.m */,
|
||||
);
|
||||
path = VM;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
049FB2162EC20A6600FAB05D /* BMLongPressDragCellCollectionView */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -1820,6 +1843,7 @@
|
||||
04791F922ED48010004E8522 /* KBNoticeVC.m in Sources */,
|
||||
04FC970F2EB334F8007BD342 /* KBWebImageManager.m in Sources */,
|
||||
04FC95CF2EB1E7A1007BD342 /* HomeVC.m in Sources */,
|
||||
0498BDDE2EE81508006CC1D5 /* KBShopVM.m in Sources */,
|
||||
049FB2112EC1F72F00FAB05D /* KBMyListCell.m in Sources */,
|
||||
A1B2D7022EB8C00100000001 /* KBLangTestVC.m in Sources */,
|
||||
04122F6D2EC5F40800EF7AB3 /* NSObject+FGIsNullOrEmpty.m in Sources */,
|
||||
@@ -1831,6 +1855,7 @@
|
||||
048908DA2EBF61AF00FABA60 /* UICollectionViewLeftAlignedLayout.m in Sources */,
|
||||
04C6EABF2EAF86530089C901 /* main.m in Sources */,
|
||||
0498BD6E2EE0285D006CC1D5 /* KBForgetVerPwdVC.m in Sources */,
|
||||
0498BDE12EEA87C9006CC1D5 /* KBShopStyleModel.m in Sources */,
|
||||
04FC95CC2EB1E780007BD342 /* BaseTabBarController.m in Sources */,
|
||||
0479205A2EDEE1FC004E8522 /* CRBoxTextView.m in Sources */,
|
||||
0479205B2EDEE1FC004E8522 /* CRSecrectImageView.m in Sources */,
|
||||
@@ -1847,6 +1872,7 @@
|
||||
049FB2432EC4BBB700FAB05D /* KBLoginPopView.m in Sources */,
|
||||
048908CC2EBE373500FABA60 /* KBSearchBarView.m in Sources */,
|
||||
04122F872EC6198C00EF7AB3 /* WMDragView.m in Sources */,
|
||||
0498BDE42EEA885D006CC1D5 /* KBShopThemeModel.m in Sources */,
|
||||
048908CD2EBE373500FABA60 /* KBSearchSectionHeader.m in Sources */,
|
||||
049FB2202EC30D2700FAB05D /* HomeRankDetailPopView.m in Sources */,
|
||||
048908CE2EBE373500FABA60 /* KBSkinCardCell.m in Sources */,
|
||||
|
||||
@@ -49,7 +49,8 @@
|
||||
|
||||
- (void)configWithTitle:(NSString *)title imageURL:(NSString *)url price:(NSString *)price {
|
||||
self.titleLabel.text = title.length ? title : @"Dopamine";
|
||||
[self.priceBtn setTitle:@"20" forState:UIControlStateNormal];
|
||||
NSString *priceText = price.length ? price : @"20";
|
||||
[self.priceBtn setTitle:priceText forState:UIControlStateNormal];
|
||||
|
||||
// 简化:本地展示占位色,无网络图
|
||||
self.coverView.backgroundColor = [UIColor colorWithWhite:0.92 alpha:1.0];
|
||||
@@ -84,4 +85,3 @@
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
20
keyBoard/Class/Shop/M/KBShopStyleModel.h
Normal file
20
keyBoard/Class/Shop/M/KBShopStyleModel.h
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// KBShopStyleModel.h
|
||||
// keyBoard
|
||||
//
|
||||
// Created by Mac on 2025/12/11.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface KBShopStyleModel : NSObject
|
||||
/// 风格 id
|
||||
@property (nonatomic, copy, nullable) NSString *styleId;
|
||||
/// 风格名称
|
||||
@property (nonatomic, copy, nullable) NSString *styleName;
|
||||
@end
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
18
keyBoard/Class/Shop/M/KBShopStyleModel.m
Normal file
18
keyBoard/Class/Shop/M/KBShopStyleModel.m
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// KBShopStyleModel.m
|
||||
// keyBoard
|
||||
//
|
||||
// Created by Mac on 2025/12/11.
|
||||
//
|
||||
|
||||
#import "KBShopStyleModel.h"
|
||||
|
||||
@implementation KBShopStyleModel
|
||||
+ (NSDictionary *)mj_replacedKeyFromPropertyName {
|
||||
return @{
|
||||
@"styleId": @"id",
|
||||
@"styleName": @"styleName"
|
||||
};
|
||||
}
|
||||
|
||||
@end
|
||||
34
keyBoard/Class/Shop/M/KBShopThemeModel.h
Normal file
34
keyBoard/Class/Shop/M/KBShopThemeModel.h
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// KBShopThemeModel.h
|
||||
// keyBoard
|
||||
//
|
||||
// Created by Mac on 2025/12/11.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface KBShopThemeModel : NSObject
|
||||
/// 主题 id
|
||||
@property (nonatomic, copy, nullable) NSString *themeId;
|
||||
/// 主题名称
|
||||
@property (nonatomic, copy, nullable) NSString *themeName;
|
||||
/// 价格(单位:接口返回的货币)
|
||||
@property (nonatomic, assign) CGFloat themePrice;
|
||||
/// 标签或风格名称
|
||||
@property (nonatomic, copy, nullable) NSString *themeTag;
|
||||
/// 下载量描述
|
||||
@property (nonatomic, copy, nullable) NSString *themeDownload;
|
||||
/// 风格类型(接口返回 themeStyle)
|
||||
@property (nonatomic, assign) NSInteger themeStyle;
|
||||
/// 是否上架
|
||||
@property (nonatomic, assign, getter=isThemeStatus) BOOL themeStatus;
|
||||
/// 购买次数
|
||||
@property (nonatomic, assign) NSInteger themePurchasesNumber;
|
||||
/// 预览图
|
||||
@property (nonatomic, copy, nullable) NSString *themePreviewImageUrl;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
16
keyBoard/Class/Shop/M/KBShopThemeModel.m
Normal file
16
keyBoard/Class/Shop/M/KBShopThemeModel.m
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// KBShopThemeModel.m
|
||||
// keyBoard
|
||||
//
|
||||
// Created by Mac on 2025/12/11.
|
||||
//
|
||||
|
||||
#import "KBShopThemeModel.h"
|
||||
|
||||
@implementation KBShopThemeModel
|
||||
+ (NSDictionary *)mj_replacedKeyFromPropertyName {
|
||||
return @{
|
||||
@"themeId": @"id",
|
||||
};
|
||||
}
|
||||
@end
|
||||
@@ -10,17 +10,25 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class KBShopStyleModel;
|
||||
@class KBShopVM;
|
||||
@class KBShopThemeModel;
|
||||
|
||||
@interface KBShopItemVC : UIViewController<JXPagerViewListViewDelegate>
|
||||
/// 列表:使用 UICollectionView 展示两列皮肤卡片
|
||||
@property (nonatomic, strong) UICollectionView *collectionView;
|
||||
/// 数据源:简单字符串作为标题(演示用)
|
||||
@property (nonatomic, strong) NSMutableArray<NSString *> *dataSource;
|
||||
/// 数据源:主题列表
|
||||
@property (nonatomic, strong) NSMutableArray<KBShopThemeModel *> *dataSource;
|
||||
/// 是否需要上拉加载更多
|
||||
@property (nonatomic, assign) BOOL isNeedFooter;
|
||||
/// 是否需要下拉刷新
|
||||
@property (nonatomic, assign) BOOL isNeedHeader;
|
||||
/// 首次是否已刷新过(避免重复触发)
|
||||
@property (nonatomic, assign) BOOL isHeaderRefreshed; // 默认为 YES
|
||||
/// 当前所属风格
|
||||
@property (nonatomic, strong, nullable) KBShopStyleModel *style;
|
||||
/// 可复用的 ShopVM(由外部传入,便于共用缓存/网络配置)
|
||||
@property (nonatomic, strong, nullable) KBShopVM *shopViewModel;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -11,9 +11,12 @@
|
||||
#import "KBSkinCardCell.h"
|
||||
#import "KBSkinInstallBridge.h"
|
||||
#import "KBHUD.h"
|
||||
#import "KBShopVM.h"
|
||||
|
||||
@interface KBShopItemVC ()<UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
|
||||
@property (nonatomic, copy) void(^scrollCallback)(UIScrollView *scrollView);
|
||||
@property (nonatomic, strong) KBShopVM *internalViewModel;
|
||||
@property (nonatomic, assign, getter=isLoading) BOOL loading;
|
||||
@end
|
||||
|
||||
@implementation KBShopItemVC
|
||||
@@ -21,6 +24,7 @@
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
self.view.backgroundColor = [UIColor clearColor];
|
||||
self.dataSource = [NSMutableArray array];
|
||||
|
||||
// 懒加载 collectionView,并添加到视图
|
||||
[self.view addSubview:self.collectionView];
|
||||
@@ -28,26 +32,19 @@
|
||||
make.edges.equalTo(self.view); // mas 布局:铺满
|
||||
}];
|
||||
|
||||
// 刷新组件(演示:2 秒后结束)
|
||||
KBWeakSelf
|
||||
if (self.isNeedHeader) {
|
||||
self.collectionView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[weakSelf.collectionView.mj_header endRefreshing];
|
||||
});
|
||||
[weakSelf fetchThemes];
|
||||
}];
|
||||
}
|
||||
if (self.isNeedFooter) {
|
||||
self.collectionView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[weakSelf.dataSource addObject:KBLocalized(@"Loaded more successfully")]; // 模拟新增一条
|
||||
[weakSelf.collectionView reloadData];
|
||||
[weakSelf.collectionView.mj_footer endRefreshing];
|
||||
});
|
||||
[weakSelf fetchThemes];
|
||||
}];
|
||||
}
|
||||
|
||||
self.collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
self.collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
|
||||
[self beginFirstRefresh];
|
||||
}
|
||||
@@ -56,23 +53,58 @@
|
||||
|
||||
// 首次进入自动触发一次刷新(如果需要)
|
||||
- (void)beginFirstRefresh {
|
||||
if (!self.isHeaderRefreshed) {
|
||||
[self beginRefreshImmediately];
|
||||
if (self.isHeaderRefreshed || self.isLoading) {
|
||||
return;
|
||||
}
|
||||
if (!self.style.styleId.length) {
|
||||
return;
|
||||
}
|
||||
[self beginRefreshImmediately];
|
||||
}
|
||||
|
||||
- (void)beginRefreshImmediately {
|
||||
if (self.isNeedHeader) {
|
||||
if (self.isNeedHeader && !self.collectionView.mj_header.isRefreshing) {
|
||||
[self.collectionView.mj_header beginRefreshing];
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
self.isHeaderRefreshed = YES;
|
||||
[self.collectionView reloadData];
|
||||
[self.collectionView.mj_header endRefreshing];
|
||||
});
|
||||
} else {
|
||||
self.isHeaderRefreshed = YES;
|
||||
[self.collectionView reloadData];
|
||||
}
|
||||
[self fetchThemes];
|
||||
}
|
||||
|
||||
- (void)fetchThemes {
|
||||
if (self.isLoading) {
|
||||
return;
|
||||
}
|
||||
NSString *styleId = self.style.styleId;
|
||||
if (!styleId.length) {
|
||||
return;
|
||||
}
|
||||
self.loading = YES;
|
||||
KBShopVM *vm = self.shopViewModel;
|
||||
if (!vm) {
|
||||
if (!self.internalViewModel) {
|
||||
self.internalViewModel = [[KBShopVM alloc] init];
|
||||
}
|
||||
vm = self.internalViewModel;
|
||||
}
|
||||
KBWeakSelf
|
||||
[vm fetchThemesForStyleId:styleId completion:^(NSArray<KBShopThemeModel *> * _Nullable themes, NSError * _Nullable error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
weakSelf.loading = NO;
|
||||
weakSelf.isHeaderRefreshed = YES;
|
||||
if (error) {
|
||||
NSString *msg = error.localizedDescription ?: KBLocalized(@"Network error");
|
||||
[KBHUD showInfo:msg];
|
||||
} else {
|
||||
weakSelf.dataSource = themes.count ? themes.mutableCopy : [NSMutableArray array];
|
||||
[weakSelf.collectionView reloadData];
|
||||
}
|
||||
if ([weakSelf.collectionView.mj_header isRefreshing]) {
|
||||
[weakSelf.collectionView.mj_header endRefreshing];
|
||||
}
|
||||
if ([weakSelf.collectionView.mj_footer isRefreshing]) {
|
||||
[weakSelf.collectionView.mj_footer endRefreshing];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - UICollectionView DataSource
|
||||
@@ -84,8 +116,10 @@
|
||||
|
||||
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
KBSkinCardCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"KBSkinCardCell" forIndexPath:indexPath];
|
||||
NSString *title = (indexPath.item < self.dataSource.count) ? self.dataSource[indexPath.item] : @"Dopamine";
|
||||
[cell configWithTitle:title imageURL:nil price:@"20"]; // 价格写死 20(演示)
|
||||
KBShopThemeModel *theme = (indexPath.item < self.dataSource.count) ? self.dataSource[indexPath.item] : nil;
|
||||
NSString *title = theme.themeName.length ? theme.themeName : KBLocalized(@"Themes");
|
||||
NSString *price = [self kb_priceStringForTheme:theme];
|
||||
[cell configWithTitle:title imageURL:nil price:price];
|
||||
return cell;
|
||||
}
|
||||
|
||||
@@ -117,8 +151,28 @@
|
||||
[self kb_handleShopTapAtIndexPath:indexPath];
|
||||
}
|
||||
|
||||
- (NSString *)kb_priceStringForTheme:(KBShopThemeModel *)theme {
|
||||
if (!theme) {
|
||||
return @"";
|
||||
}
|
||||
if (theme.themePrice <= 0.0f) {
|
||||
return KBLocalized(@"Free");
|
||||
}
|
||||
static NSNumberFormatter *formatter;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
formatter = [[NSNumberFormatter alloc] init];
|
||||
formatter.minimumFractionDigits = 0;
|
||||
formatter.maximumFractionDigits = 2;
|
||||
formatter.minimumIntegerDigits = 1;
|
||||
});
|
||||
NSString *priceString = [formatter stringFromNumber:@(theme.themePrice)];
|
||||
return priceString ?: [NSString stringWithFormat:@"%.2f", theme.themePrice];
|
||||
}
|
||||
|
||||
- (void)kb_handleShopTapAtIndexPath:(NSIndexPath *)indexPath {
|
||||
NSString *title = (indexPath.item < self.dataSource.count) ? self.dataSource[indexPath.item] : KBLocalized(@"专属皮肤002");
|
||||
KBShopThemeModel *theme = (indexPath.item < self.dataSource.count) ? self.dataSource[indexPath.item] : nil;
|
||||
NSString *title = theme.themeName.length ? theme.themeName : KBLocalized(@"专属皮肤002");
|
||||
// 将需求固定到 002.zip,本地写死皮肤 id,便于键盘扩展识别并解压。
|
||||
static NSString * const kKBBundleSkinId002 = @"bundle_skin_fense";
|
||||
[KBSkinInstallBridge publishBundleSkinRequestWithId:kKBBundleSkinId002
|
||||
@@ -128,6 +182,13 @@
|
||||
[KBHUD showInfo:KBLocalized(@"已通知键盘解压,切换到自定义键盘即可生效")];
|
||||
}
|
||||
|
||||
- (void)setStyle:(KBShopStyleModel *)style {
|
||||
_style = style;
|
||||
if (self.isViewLoaded && !self.isHeaderRefreshed) {
|
||||
[self beginFirstRefresh];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UIScrollView Delegate(转发给分页容器)
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
||||
!self.scrollCallback ?: self.scrollCallback(scrollView);
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
|
||||
#import "KBWebViewViewController.h"
|
||||
#import "KBShopVM.h"
|
||||
#import "KBHUD.h"
|
||||
|
||||
static const CGFloat JXTableHeaderViewHeight = (323);
|
||||
static const CGFloat JXheightForHeaderInSection = 50;
|
||||
@@ -33,11 +35,13 @@ static const CGFloat JXheightForHeaderInSection = 50;
|
||||
|
||||
@property (nonatomic, strong) JXCategoryTitleView *categoryView;
|
||||
@property (nonatomic, strong) NSArray <NSString *> *titles;
|
||||
@property (nonatomic, copy) NSArray<KBShopStyleModel *> *styles;
|
||||
@property (nonatomic, strong) UIImageView *bgImageView; // 全屏背景图
|
||||
@property (nonatomic, strong) UIButton *searchBtn;
|
||||
@property (nonatomic, strong) UIButton *skinBtn;
|
||||
// 记录当前分类条是否为白底,避免重复设置
|
||||
@property (nonatomic, assign) BOOL categoryIsWhite;
|
||||
@property (nonatomic, strong) KBShopVM *shopVM;
|
||||
|
||||
@end
|
||||
|
||||
@@ -65,6 +69,7 @@ static const CGFloat JXheightForHeaderInSection = 50;
|
||||
make.width.height.mas_equalTo(25);
|
||||
}];
|
||||
|
||||
[self fetchShopStylesWithHUD:YES];
|
||||
}
|
||||
|
||||
// 系统导航栏显隐由 Base 统一管理(全局隐藏),该 VC 不再手动切换,避免闪烁。
|
||||
@@ -76,10 +81,8 @@ static const CGFloat JXheightForHeaderInSection = 50;
|
||||
self.view.backgroundColor = [UIColor whiteColor];
|
||||
self.navigationController.navigationBar.translucent = false;
|
||||
self.edgesForExtendedLayout = UIRectEdgeNone;
|
||||
_titles = @[KBLocalized(@"能力"), KBLocalized(@"爱好"), KBLocalized(@"队友"),
|
||||
KBLocalized(@"能力2"), KBLocalized(@"爱好2"), KBLocalized(@"队友2"),
|
||||
KBLocalized(@"能力"), KBLocalized(@"爱好"), KBLocalized(@"队友"),
|
||||
KBLocalized(@"能力2"), KBLocalized(@"爱好2"), KBLocalized(@"队友2")];
|
||||
self.styles = @[];
|
||||
_titles = @[];
|
||||
|
||||
_userHeaderView = [[KBShopHeadView alloc] init];
|
||||
_categoryView = (JXCategoryTitleView *)[[KBCategoryTitleView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, JXheightForHeaderInSection)];
|
||||
@@ -133,13 +136,7 @@ static const CGFloat JXheightForHeaderInSection = 50;
|
||||
|
||||
__weak typeof(self)weakSelf = self;
|
||||
self.pagerView.mainTableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
self.categoryView.titles = @[KBLocalized(@"高级能力"), KBLocalized(@"高级爱好"), KBLocalized(@"高级队友")];
|
||||
self.categoryView.defaultSelectedIndex = 0;
|
||||
[self.categoryView reloadData];
|
||||
[self.pagerView reloadData];
|
||||
[weakSelf.pagerView.mainTableView.mj_header endRefreshing];
|
||||
});
|
||||
[weakSelf fetchShopStylesWithHUD:NO];
|
||||
}];
|
||||
|
||||
self.pagerView.pinSectionHeaderVerticalOffset = KB_NAV_TOTAL_HEIGHT;
|
||||
@@ -194,19 +191,9 @@ static const CGFloat JXheightForHeaderInSection = 50;
|
||||
list.title = self.titles[index];
|
||||
list.isNeedHeader = self.isNeedHeader;
|
||||
list.isNeedFooter = self.isNeedFooter;
|
||||
if (index == 0) {
|
||||
list.dataSource = @[KBLocalized(@"橡胶火箭"), KBLocalized(@"橡胶火箭炮"), KBLocalized(@"橡胶机关枪"), KBLocalized(@"橡胶子弹"),
|
||||
KBLocalized(@"橡胶攻城炮"), KBLocalized(@"橡胶象枪"), KBLocalized(@"橡胶象枪乱打"), KBLocalized(@"橡胶灰熊铳"),
|
||||
KBLocalized(@"橡胶雷神象枪"), KBLocalized(@"橡胶猿王枪"), KBLocalized(@"橡胶犀·榴弹炮"), KBLocalized(@"橡胶大蛇炮"),
|
||||
KBLocalized(@"橡胶火箭"), KBLocalized(@"橡胶火箭炮"), KBLocalized(@"橡胶机关枪"), KBLocalized(@"橡胶子弹"),
|
||||
KBLocalized(@"橡胶攻城炮"), KBLocalized(@"橡胶象枪"), KBLocalized(@"橡胶象枪乱打"), KBLocalized(@"橡胶灰熊铳"),
|
||||
KBLocalized(@"橡胶雷神象枪"), KBLocalized(@"橡胶猿王枪"), KBLocalized(@"橡胶犀·榴弹炮"), KBLocalized(@"橡胶大蛇炮")].mutableCopy;
|
||||
}else if (index == 1) {
|
||||
list.dataSource = @[KBLocalized(@"吃烤肉"), KBLocalized(@"吃鸡腿肉"), KBLocalized(@"吃牛肉"), KBLocalized(@"各种肉")].mutableCopy;
|
||||
}else {
|
||||
list.dataSource = @[KBLocalized(@"【剑士】罗罗诺亚·索隆"), KBLocalized(@"【航海士】娜美"), KBLocalized(@"【狙击手】乌索普"),
|
||||
KBLocalized(@"【厨师】香吉士"), KBLocalized(@"【船医】托尼托尼·乔巴"), KBLocalized(@"【船匠】 弗兰奇"),
|
||||
KBLocalized(@"【音乐家】布鲁克"), KBLocalized(@"【考古学家】妮可·罗宾")].mutableCopy;
|
||||
list.shopViewModel = self.shopVM;
|
||||
if (index < self.styles.count) {
|
||||
list.style = self.styles[index];
|
||||
}
|
||||
return list;
|
||||
}
|
||||
@@ -277,4 +264,46 @@ static const CGFloat JXheightForHeaderInSection = 50;
|
||||
}
|
||||
return _skinBtn;
|
||||
}
|
||||
|
||||
- (KBShopVM *)shopVM {
|
||||
if (!_shopVM) {
|
||||
_shopVM = [[KBShopVM alloc] init];
|
||||
}
|
||||
return _shopVM;
|
||||
}
|
||||
|
||||
- (void)fetchShopStylesWithHUD:(BOOL)showHUD {
|
||||
if (showHUD) {
|
||||
[KBHUD show];
|
||||
}
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[self.shopVM fetchAllStylesWithCompletion:^(NSArray<KBShopStyleModel *> * _Nullable styles, NSError * _Nullable error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (showHUD) {
|
||||
[KBHUD dismiss];
|
||||
}
|
||||
[weakSelf.pagerView.mainTableView.mj_header endRefreshing];
|
||||
if (error) {
|
||||
NSString *msg = error.localizedDescription ?: KBLocalized(@"Network error");
|
||||
[KBHUD showInfo:msg];
|
||||
return;
|
||||
}
|
||||
[weakSelf applyStyles:styles];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)applyStyles:(NSArray<KBShopStyleModel *> *)styles {
|
||||
self.styles = styles ?: @[];
|
||||
NSMutableArray *names = [NSMutableArray array];
|
||||
for (KBShopStyleModel *style in self.styles) {
|
||||
NSString *name = style.styleName.length ? style.styleName : KBLocalized(@"Themes");
|
||||
[names addObject:name];
|
||||
}
|
||||
self.titles = names.copy;
|
||||
self.categoryView.titles = self.titles;
|
||||
self.categoryView.defaultSelectedIndex = 0;
|
||||
[self.categoryView reloadData];
|
||||
[self.pagerView reloadData];
|
||||
}
|
||||
@end
|
||||
|
||||
34
keyBoard/Class/Shop/VM/KBShopVM.h
Normal file
34
keyBoard/Class/Shop/VM/KBShopVM.h
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// KBShopVM.h
|
||||
// keyBoard
|
||||
//
|
||||
// Created by Mac on 2025/12/9.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
#import "KBShopStyleModel.h"
|
||||
#import "KBShopThemeModel.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#pragma mark - Models
|
||||
|
||||
typedef void(^KBShopStylesCompletion)(NSArray<KBShopStyleModel *> *_Nullable styles,
|
||||
NSError *_Nullable error);
|
||||
typedef void(^KBShopThemesCompletion)(NSArray<KBShopThemeModel *> *_Nullable themes,
|
||||
NSError *_Nullable error);
|
||||
|
||||
@interface KBShopVM : NSObject
|
||||
@property (nonatomic, copy, readonly, nullable) NSArray<KBShopStyleModel *> *styles;
|
||||
|
||||
/// 查询全部主题风格
|
||||
- (void)fetchAllStylesWithCompletion:(KBShopStylesCompletion)completion;
|
||||
|
||||
/// 按风格请求主题列表
|
||||
- (void)fetchThemesForStyleId:(nullable NSString *)styleId
|
||||
completion:(KBShopThemesCompletion)completion;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
85
keyBoard/Class/Shop/VM/KBShopVM.m
Normal file
85
keyBoard/Class/Shop/VM/KBShopVM.m
Normal file
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// KBShopVM.m
|
||||
// keyBoard
|
||||
//
|
||||
// Created by Mac on 2025/12/9.
|
||||
//
|
||||
|
||||
#import "KBShopVM.h"
|
||||
#import "KBNetworkManager.h"
|
||||
#import "KBAPI.h"
|
||||
#import "KBBizCode.h"
|
||||
#import <MJExtension/MJExtension.h>
|
||||
|
||||
@interface KBShopVM ()
|
||||
@property (nonatomic, copy, readwrite, nullable) NSArray<KBShopStyleModel *> *styles;
|
||||
@end
|
||||
|
||||
|
||||
@implementation KBShopVM
|
||||
|
||||
- (NSError *)kb_invalidResponseError {
|
||||
return [NSError errorWithDomain:KBNetworkErrorDomain
|
||||
code:KBNetworkErrorInvalidResponse
|
||||
userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Invalid response")}];
|
||||
}
|
||||
|
||||
- (NSError *)kb_invalidParameterError {
|
||||
return [NSError errorWithDomain:KBNetworkErrorDomain
|
||||
code:KBNetworkErrorInvalidResponse
|
||||
userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Invalid parameter")}];
|
||||
}
|
||||
|
||||
- (void)fetchAllStylesWithCompletion:(KBShopStylesCompletion)completion {
|
||||
[[KBNetworkManager shared] GET:API_THEME_LIST_ALL_STYLES
|
||||
parameters:nil
|
||||
headers:nil
|
||||
autoShowBusinessError:NO
|
||||
completion:^(NSDictionary * _Nullable json,
|
||||
NSURLResponse * _Nullable response,
|
||||
NSError * _Nullable error) {
|
||||
if (error) {
|
||||
if (completion) completion(nil, error);
|
||||
return;
|
||||
}
|
||||
id dataObj = json[KBData] ?: json[@"data"];
|
||||
if (![dataObj isKindOfClass:[NSArray class]]) {
|
||||
if (completion) completion(nil, [self kb_invalidResponseError]);
|
||||
return;
|
||||
}
|
||||
NSArray<KBShopStyleModel *> *list = [KBShopStyleModel mj_objectArrayWithKeyValuesArray:(NSArray *)dataObj];
|
||||
self.styles = list;
|
||||
if (completion) completion(list, nil);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)fetchThemesForStyleId:(nullable NSString *)styleId
|
||||
completion:(KBShopThemesCompletion)completion {
|
||||
if (styleId.length == 0) {
|
||||
if (completion) completion(nil, [self kb_invalidParameterError]);
|
||||
return;
|
||||
}
|
||||
NSMutableDictionary *params = [NSMutableDictionary dictionary];
|
||||
params[@"themeStyle"] = @([styleId integerValue]);
|
||||
[[KBNetworkManager shared] GET:API_THEME_LIST_BY_STYLE
|
||||
parameters:params
|
||||
headers:nil
|
||||
autoShowBusinessError:NO
|
||||
completion:^(NSDictionary * _Nullable json,
|
||||
NSURLResponse * _Nullable response,
|
||||
NSError * _Nullable error) {
|
||||
if (error) {
|
||||
if (completion) completion(nil, error);
|
||||
return;
|
||||
}
|
||||
id dataObj = json[KBData] ?: json[@"data"];
|
||||
if (![dataObj isKindOfClass:[NSArray class]]) {
|
||||
if (completion) completion(nil, [self kb_invalidResponseError]);
|
||||
return;
|
||||
}
|
||||
NSArray<KBShopThemeModel *> *list = [KBShopThemeModel mj_objectArrayWithKeyValuesArray:(NSArray *)dataObj];
|
||||
if (completion) completion(list, nil);
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user