新增搜索
This commit is contained in:
@@ -54,6 +54,7 @@
|
|||||||
#define API_THEME_PURCHASE @"/themes/purchase" // 购买主题
|
#define API_THEME_PURCHASE @"/themes/purchase" // 购买主题
|
||||||
#define API_THEME_DOWNLOAD @"/themes/download" // 主题下载信息
|
#define API_THEME_DOWNLOAD @"/themes/download" // 主题下载信息
|
||||||
#define API_THEME_RECOMMENDED @"/themes/recommended" // 推荐主题列表
|
#define API_THEME_RECOMMENDED @"/themes/recommended" // 推荐主题列表
|
||||||
|
#define API_THEME_SEARCH @"/themes/search" // 搜索主题(themeName)
|
||||||
|
|
||||||
/// pay
|
/// pay
|
||||||
#define API_VALIDATE_RECEIPT @"/apple/validate-receipt" // 排行榜标签列表
|
#define API_VALIDATE_RECEIPT @"/apple/validate-receipt" // 排行榜标签列表
|
||||||
|
|||||||
@@ -95,11 +95,13 @@
|
|||||||
048908CE2EBE373500FABA60 /* KBSkinCardCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908C92EBE373500FABA60 /* KBSkinCardCell.m */; };
|
048908CE2EBE373500FABA60 /* KBSkinCardCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908C92EBE373500FABA60 /* KBSkinCardCell.m */; };
|
||||||
048908CF2EBE373500FABA60 /* KBTagCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908CB2EBE373500FABA60 /* KBTagCell.m */; };
|
048908CF2EBE373500FABA60 /* KBTagCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908CB2EBE373500FABA60 /* KBTagCell.m */; };
|
||||||
048908D22EBF611D00FABA60 /* KBHistoryMoreCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908D12EBF611D00FABA60 /* KBHistoryMoreCell.m */; };
|
048908D22EBF611D00FABA60 /* KBHistoryMoreCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908D12EBF611D00FABA60 /* KBHistoryMoreCell.m */; };
|
||||||
048908DA2EBF61AF00FABA60 /* UICollectionViewLeftAlignedLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908D82EBF61AF00FABA60 /* UICollectionViewLeftAlignedLayout.m */; };
|
048908DA2EBF61AF00FABA60 /* UICollectionViewLeftAlignedLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908D82EBF61AF00FABA60 /* UICollectionViewLeftAlignedLayout.m */; };
|
||||||
048908DD2EBF67EB00FABA60 /* KBSearchResultVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908DC2EBF67EB00FABA60 /* KBSearchResultVC.m */; };
|
048908DD2EBF67EB00FABA60 /* KBSearchResultVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908DC2EBF67EB00FABA60 /* KBSearchResultVC.m */; };
|
||||||
048908E02EBF73DC00FABA60 /* MySkinVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908DF2EBF73DC00FABA60 /* MySkinVC.m */; };
|
05A1B2D12F5B1A2B3C4D5E60 /* KBSearchVM.m in Sources */ = {isa = PBXBuildFile; fileRef = 05A1B2C52F5B1A2B3C4D5E60 /* KBSearchVM.m */; };
|
||||||
048908E32EBF760000FABA60 /* MySkinCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908E22EBF760000FABA60 /* MySkinCell.m */; };
|
05A1B2D22F5B1A2B3C4D5E60 /* KBSearchThemeModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 05A1B2C72F5B1A2B3C4D5E60 /* KBSearchThemeModel.m */; };
|
||||||
048908E32EBF821700FABA60 /* KBSkinDetailVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908E22EBF821700FABA60 /* KBSkinDetailVC.m */; };
|
048908E02EBF73DC00FABA60 /* MySkinVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908DF2EBF73DC00FABA60 /* MySkinVC.m */; };
|
||||||
|
048908E32EBF760000FABA60 /* MySkinCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908E22EBF760000FABA60 /* MySkinCell.m */; };
|
||||||
|
048908E32EBF821700FABA60 /* KBSkinDetailVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908E22EBF821700FABA60 /* KBSkinDetailVC.m */; };
|
||||||
048908E62EBF841B00FABA60 /* KBSkinDetailTagCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908E52EBF841B00FABA60 /* KBSkinDetailTagCell.m */; };
|
048908E62EBF841B00FABA60 /* KBSkinDetailTagCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908E52EBF841B00FABA60 /* KBSkinDetailTagCell.m */; };
|
||||||
048908E92EBF843000FABA60 /* KBSkinDetailHeaderCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908E82EBF843000FABA60 /* KBSkinDetailHeaderCell.m */; };
|
048908E92EBF843000FABA60 /* KBSkinDetailHeaderCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908E82EBF843000FABA60 /* KBSkinDetailHeaderCell.m */; };
|
||||||
048908EC2EBF849300FABA60 /* KBSkinTagsContainerCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908EB2EBF849300FABA60 /* KBSkinTagsContainerCell.m */; };
|
048908EC2EBF849300FABA60 /* KBSkinTagsContainerCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 048908EB2EBF849300FABA60 /* KBSkinTagsContainerCell.m */; };
|
||||||
@@ -385,12 +387,16 @@
|
|||||||
048908D02EBF611D00FABA60 /* KBHistoryMoreCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBHistoryMoreCell.h; sourceTree = "<group>"; };
|
048908D02EBF611D00FABA60 /* KBHistoryMoreCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBHistoryMoreCell.h; sourceTree = "<group>"; };
|
||||||
048908D12EBF611D00FABA60 /* KBHistoryMoreCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBHistoryMoreCell.m; sourceTree = "<group>"; };
|
048908D12EBF611D00FABA60 /* KBHistoryMoreCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBHistoryMoreCell.m; sourceTree = "<group>"; };
|
||||||
048908D72EBF61AF00FABA60 /* UICollectionViewLeftAlignedLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UICollectionViewLeftAlignedLayout.h; sourceTree = "<group>"; };
|
048908D72EBF61AF00FABA60 /* UICollectionViewLeftAlignedLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UICollectionViewLeftAlignedLayout.h; sourceTree = "<group>"; };
|
||||||
048908D82EBF61AF00FABA60 /* UICollectionViewLeftAlignedLayout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UICollectionViewLeftAlignedLayout.m; sourceTree = "<group>"; };
|
048908D82EBF61AF00FABA60 /* UICollectionViewLeftAlignedLayout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UICollectionViewLeftAlignedLayout.m; sourceTree = "<group>"; };
|
||||||
048908DB2EBF67EB00FABA60 /* KBSearchResultVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSearchResultVC.h; sourceTree = "<group>"; };
|
048908DB2EBF67EB00FABA60 /* KBSearchResultVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSearchResultVC.h; sourceTree = "<group>"; };
|
||||||
048908DC2EBF67EB00FABA60 /* KBSearchResultVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSearchResultVC.m; sourceTree = "<group>"; };
|
048908DC2EBF67EB00FABA60 /* KBSearchResultVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSearchResultVC.m; sourceTree = "<group>"; };
|
||||||
048908DE2EBF73DC00FABA60 /* MySkinVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MySkinVC.h; sourceTree = "<group>"; };
|
05A1B2C42F5B1A2B3C4D5E60 /* KBSearchVM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSearchVM.h; sourceTree = "<group>"; };
|
||||||
048908DF2EBF73DC00FABA60 /* MySkinVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MySkinVC.m; sourceTree = "<group>"; };
|
05A1B2C52F5B1A2B3C4D5E60 /* KBSearchVM.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSearchVM.m; sourceTree = "<group>"; };
|
||||||
048908E12EBF760000FABA60 /* MySkinCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MySkinCell.h; sourceTree = "<group>"; };
|
05A1B2C62F5B1A2B3C4D5E60 /* KBSearchThemeModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSearchThemeModel.h; sourceTree = "<group>"; };
|
||||||
|
05A1B2C72F5B1A2B3C4D5E60 /* KBSearchThemeModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSearchThemeModel.m; sourceTree = "<group>"; };
|
||||||
|
048908DE2EBF73DC00FABA60 /* MySkinVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MySkinVC.h; sourceTree = "<group>"; };
|
||||||
|
048908DF2EBF73DC00FABA60 /* MySkinVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MySkinVC.m; sourceTree = "<group>"; };
|
||||||
|
048908E12EBF760000FABA60 /* MySkinCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MySkinCell.h; sourceTree = "<group>"; };
|
||||||
048908E12EBF821700FABA60 /* KBSkinDetailVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinDetailVC.h; sourceTree = "<group>"; };
|
048908E12EBF821700FABA60 /* KBSkinDetailVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinDetailVC.h; sourceTree = "<group>"; };
|
||||||
048908E22EBF760000FABA60 /* MySkinCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MySkinCell.m; sourceTree = "<group>"; };
|
048908E22EBF760000FABA60 /* MySkinCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MySkinCell.m; sourceTree = "<group>"; };
|
||||||
048908E22EBF821700FABA60 /* KBSkinDetailVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinDetailVC.m; sourceTree = "<group>"; };
|
048908E22EBF821700FABA60 /* KBSkinDetailVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinDetailVC.m; sourceTree = "<group>"; };
|
||||||
@@ -926,13 +932,15 @@
|
|||||||
path = Common;
|
path = Common;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
048908BD2EBE329D00FABA60 /* M */ = {
|
048908BD2EBE329D00FABA60 /* M */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
);
|
05A1B2C62F5B1A2B3C4D5E60 /* KBSearchThemeModel.h */,
|
||||||
path = M;
|
05A1B2C72F5B1A2B3C4D5E60 /* KBSearchThemeModel.m */,
|
||||||
sourceTree = "<group>";
|
);
|
||||||
};
|
path = M;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
048908BE2EBE329D00FABA60 /* V */ = {
|
048908BE2EBE329D00FABA60 /* V */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -964,6 +972,7 @@
|
|||||||
048908C02EBE329D00FABA60 /* Search */ = {
|
048908C02EBE329D00FABA60 /* Search */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
04DC5CED2EF2D2C400F1AC80 /* VM */,
|
||||||
048908BD2EBE329D00FABA60 /* M */,
|
048908BD2EBE329D00FABA60 /* M */,
|
||||||
048908BE2EBE329D00FABA60 /* V */,
|
048908BE2EBE329D00FABA60 /* V */,
|
||||||
048908BF2EBE329D00FABA60 /* VC */,
|
048908BF2EBE329D00FABA60 /* VC */,
|
||||||
@@ -1178,6 +1187,15 @@
|
|||||||
path = Buy;
|
path = Buy;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
04DC5CED2EF2D2C400F1AC80 /* VM */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
05A1B2C42F5B1A2B3C4D5E60 /* KBSearchVM.h */,
|
||||||
|
05A1B2C52F5B1A2B3C4D5E60 /* KBSearchVM.m */,
|
||||||
|
);
|
||||||
|
path = VM;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
04FC95662EB0546C007BD342 /* Model */ = {
|
04FC95662EB0546C007BD342 /* Model */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -1958,11 +1976,13 @@
|
|||||||
0477BDF72EBC63A80055D639 /* KBTestVC.m in Sources */,
|
0477BDF72EBC63A80055D639 /* KBTestVC.m in Sources */,
|
||||||
04122F7E2EC5FC5500EF7AB3 /* KBJfPayCell.m in Sources */,
|
04122F7E2EC5FC5500EF7AB3 /* KBJfPayCell.m in Sources */,
|
||||||
049FB2402EC4B6EF00FAB05D /* KBULBridgeNotification.m in Sources */,
|
049FB2402EC4B6EF00FAB05D /* KBULBridgeNotification.m in Sources */,
|
||||||
04FC95C92EB1E4C9007BD342 /* BaseNavigationController.m in Sources */,
|
04FC95C92EB1E4C9007BD342 /* BaseNavigationController.m in Sources */,
|
||||||
048908DD2EBF67EB00FABA60 /* KBSearchResultVC.m in Sources */,
|
048908DD2EBF67EB00FABA60 /* KBSearchResultVC.m in Sources */,
|
||||||
047C65102EBCA8DD0035E841 /* HomeRankContentVC.m in Sources */,
|
05A1B2D12F5B1A2B3C4D5E60 /* KBSearchVM.m in Sources */,
|
||||||
047C655C2EBCD0F80035E841 /* UIView+KBShadow.m in Sources */,
|
05A1B2D22F5B1A2B3C4D5E60 /* KBSearchThemeModel.m in Sources */,
|
||||||
049FB2262EC3136D00FAB05D /* KBPersonInfoItemCell.m in Sources */,
|
047C65102EBCA8DD0035E841 /* HomeRankContentVC.m in Sources */,
|
||||||
|
047C655C2EBCD0F80035E841 /* UIView+KBShadow.m in Sources */,
|
||||||
|
049FB2262EC3136D00FAB05D /* KBPersonInfoItemCell.m in Sources */,
|
||||||
048908C32EBE32B800FABA60 /* KBSearchVC.m in Sources */,
|
048908C32EBE32B800FABA60 /* KBSearchVC.m in Sources */,
|
||||||
049FB20B2EC1C13800FAB05D /* KBSkinBottomActionView.m in Sources */,
|
049FB20B2EC1C13800FAB05D /* KBSkinBottomActionView.m in Sources */,
|
||||||
047C655E2EBCD5B20035E841 /* UIImage+KBColor.m in Sources */,
|
047C655E2EBCD5B20035E841 /* UIImage+KBColor.m in Sources */,
|
||||||
|
|||||||
34
keyBoard/Class/Search/M/KBSearchThemeModel.h
Normal file
34
keyBoard/Class/Search/M/KBSearchThemeModel.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// KBSearchThemeModel.h
|
||||||
|
// keyBoard
|
||||||
|
//
|
||||||
|
// Created by Mac on 2025/12/17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <CoreGraphics/CoreGraphics.h>
|
||||||
|
|
||||||
|
@class KBShopThemeTagModel;
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/// /themes/search 返回的主题模型
|
||||||
|
@interface KBSearchThemeModel : NSObject
|
||||||
|
|
||||||
|
@property (nonatomic, copy, nullable) NSString *themeId;
|
||||||
|
@property (nonatomic, copy, nullable) NSString *themeName;
|
||||||
|
@property (nonatomic, assign) CGFloat themePrice;
|
||||||
|
@property (nonatomic, strong, nullable) NSArray<KBShopThemeTagModel *> *themeTag;
|
||||||
|
@property (nonatomic, copy, nullable) NSString *themeDownload;
|
||||||
|
@property (nonatomic, assign) NSInteger themeStyle;
|
||||||
|
@property (nonatomic, copy, nullable) NSString *themePreviewImageUrl;
|
||||||
|
@property (nonatomic, copy, nullable) NSString *themeDownloadUrl;
|
||||||
|
@property (nonatomic, assign) NSInteger themePurchasesNumber;
|
||||||
|
@property (nonatomic, assign) NSInteger sort;
|
||||||
|
@property (nonatomic, assign) BOOL isFree;
|
||||||
|
@property (nonatomic, assign) BOOL isPurchased;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
||||||
27
keyBoard/Class/Search/M/KBSearchThemeModel.m
Normal file
27
keyBoard/Class/Search/M/KBSearchThemeModel.m
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// KBSearchThemeModel.m
|
||||||
|
// keyBoard
|
||||||
|
//
|
||||||
|
// Created by Mac on 2025/12/17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "KBSearchThemeModel.h"
|
||||||
|
#import "KBShopThemeTagModel.h"
|
||||||
|
#import <MJExtension/MJExtension.h>
|
||||||
|
|
||||||
|
@implementation KBSearchThemeModel
|
||||||
|
|
||||||
|
+ (NSDictionary *)mj_objectClassInArray {
|
||||||
|
return @{
|
||||||
|
@"themeTag": KBShopThemeTagModel.class
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSDictionary *)mj_replacedKeyFromPropertyName {
|
||||||
|
return @{
|
||||||
|
@"themeId": @"id",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@@ -6,6 +6,8 @@
|
|||||||
#import "KBSearchResultVC.h"
|
#import "KBSearchResultVC.h"
|
||||||
#import "KBSearchBarView.h"
|
#import "KBSearchBarView.h"
|
||||||
#import "KBSkinCardCell.h"
|
#import "KBSkinCardCell.h"
|
||||||
|
#import "KBSearchVM.h"
|
||||||
|
#import "KBSearchThemeModel.h"
|
||||||
|
|
||||||
static NSString * const kResultCellId = @"KBSkinCardCell";
|
static NSString * const kResultCellId = @"KBSkinCardCell";
|
||||||
|
|
||||||
@@ -20,8 +22,9 @@ static NSString * const kResultCellId = @"KBSkinCardCell";
|
|||||||
@property (nonatomic, strong) UICollectionView *collectionView;
|
@property (nonatomic, strong) UICollectionView *collectionView;
|
||||||
@property (nonatomic, strong) UICollectionViewFlowLayout *flowLayout;
|
@property (nonatomic, strong) UICollectionViewFlowLayout *flowLayout;
|
||||||
|
|
||||||
// 数据源(示例数据,实际项目中由网络返回)
|
// 数据源
|
||||||
@property (nonatomic, strong) NSMutableArray<NSDictionary *> *resultItems; // @{title, price}
|
@property (nonatomic, strong) NSMutableArray<KBSearchThemeModel *> *resultItems;
|
||||||
|
@property (nonatomic, strong) KBSearchVM *viewModel;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@@ -66,9 +69,6 @@ static NSString * const kResultCellId = @"KBSkinCardCell";
|
|||||||
if (self.defaultKeyword.length > 0) {
|
if (self.defaultKeyword.length > 0) {
|
||||||
[self.searchBarView updateKeyword:self.defaultKeyword];
|
[self.searchBarView updateKeyword:self.defaultKeyword];
|
||||||
[self performSearch:self.defaultKeyword];
|
[self performSearch:self.defaultKeyword];
|
||||||
} else {
|
|
||||||
// 填充一些示例数据
|
|
||||||
[self loadMockData];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,23 +76,22 @@ static NSString * const kResultCellId = @"KBSkinCardCell";
|
|||||||
|
|
||||||
#pragma mark - Private
|
#pragma mark - Private
|
||||||
|
|
||||||
/// 执行搜索(示例:本地生成一些数据)
|
/// 执行搜索
|
||||||
- (void)performSearch:(NSString *)keyword {
|
- (void)performSearch:(NSString *)keyword {
|
||||||
// 这里可以发起网络请求;演示中生成 10 条假数据
|
__weak typeof(self) weakSelf = self;
|
||||||
[self.resultItems removeAllObjects];
|
[self.viewModel searchThemesWithName:keyword completion:^(NSArray<KBSearchThemeModel *> * _Nullable themes, NSError * _Nullable error) {
|
||||||
for (int i = 0; i < 10; i++) {
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[self.resultItems addObject:@{ @"title": @"Dopamine", @"price": @"20" }];
|
if (error) {
|
||||||
}
|
NSLog(@"[KBSearchResultVC] search failed: %@", error);
|
||||||
[self.collectionView reloadData];
|
return;
|
||||||
}
|
}
|
||||||
|
[weakSelf.resultItems removeAllObjects];
|
||||||
/// 示例数据
|
if (themes.count > 0) {
|
||||||
- (void)loadMockData {
|
[weakSelf.resultItems addObjectsFromArray:themes];
|
||||||
[self.resultItems removeAllObjects];
|
}
|
||||||
for (int i = 0; i < 12; i++) {
|
[weakSelf.collectionView reloadData];
|
||||||
[self.resultItems addObject:@{ @"title": @"Dopamine", @"price": @"20" }];
|
});
|
||||||
}
|
}];
|
||||||
[self.collectionView reloadData];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - UICollectionViewDataSource
|
#pragma mark - UICollectionViewDataSource
|
||||||
@@ -107,8 +106,10 @@ static NSString * const kResultCellId = @"KBSkinCardCell";
|
|||||||
|
|
||||||
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||||
KBSkinCardCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kResultCellId forIndexPath:indexPath];
|
KBSkinCardCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kResultCellId forIndexPath:indexPath];
|
||||||
NSDictionary *it = self.resultItems[indexPath.item];
|
KBSearchThemeModel *model = self.resultItems[indexPath.item];
|
||||||
[cell configWithTitle:it[@"title"] imageURL:nil price:it[@"price"]];
|
[cell configWithTitle:model.themeName ?: @""
|
||||||
|
imageURL:model.themePreviewImageUrl
|
||||||
|
price:[self priceTextForTheme:model]];
|
||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,6 +151,13 @@ static NSString * const kResultCellId = @"KBSkinCardCell";
|
|||||||
return _searchBarView;
|
return _searchBarView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSString *)priceTextForTheme:(KBSearchThemeModel *)model {
|
||||||
|
if (model.themePrice > 0.0) {
|
||||||
|
return [NSString stringWithFormat:@"%.2f", model.themePrice];
|
||||||
|
}
|
||||||
|
return @"0";
|
||||||
|
}
|
||||||
|
|
||||||
- (UIView *)topBar {
|
- (UIView *)topBar {
|
||||||
if (!_topBar) {
|
if (!_topBar) {
|
||||||
_topBar = [[UIView alloc] init];
|
_topBar = [[UIView alloc] init];
|
||||||
@@ -199,11 +207,18 @@ static NSString * const kResultCellId = @"KBSkinCardCell";
|
|||||||
return _collectionView;
|
return _collectionView;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSMutableArray<NSDictionary *> *)resultItems {
|
- (NSMutableArray<KBSearchThemeModel *> *)resultItems {
|
||||||
if (!_resultItems) {
|
if (!_resultItems) {
|
||||||
_resultItems = [NSMutableArray array];
|
_resultItems = [NSMutableArray array];
|
||||||
}
|
}
|
||||||
return _resultItems;
|
return _resultItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (KBSearchVM *)viewModel {
|
||||||
|
if (!_viewModel) {
|
||||||
|
_viewModel = [[KBSearchVM alloc] init];
|
||||||
|
}
|
||||||
|
return _viewModel;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
#import "KBHistoryMoreCell.h"
|
#import "KBHistoryMoreCell.h"
|
||||||
#import "KBSearchResultVC.h"
|
#import "KBSearchResultVC.h"
|
||||||
#import "UICollectionViewLeftAlignedLayout.h"
|
#import "UICollectionViewLeftAlignedLayout.h"
|
||||||
|
#import "KBSearchVM.h"
|
||||||
|
#import "KBShopThemeModel.h"
|
||||||
|
|
||||||
|
|
||||||
static NSString * const kTagCellId = @"KBTagCell";
|
static NSString * const kTagCellId = @"KBTagCell";
|
||||||
@@ -37,9 +39,10 @@ typedef NS_ENUM(NSInteger, KBSearchSection) {
|
|||||||
|
|
||||||
// 数据
|
// 数据
|
||||||
@property (nonatomic, strong) NSMutableArray<NSString *> *historyWords; // 历史搜索
|
@property (nonatomic, strong) NSMutableArray<NSString *> *historyWords; // 历史搜索
|
||||||
@property (nonatomic, strong) NSArray<NSDictionary *> *recommendItems; // 推荐数据(title/price)
|
@property (nonatomic, copy) NSArray<KBShopThemeModel *> *recommendedThemes; // 推荐主题
|
||||||
@property (nonatomic, assign) BOOL historyExpanded; // 是否展开所有历史
|
@property (nonatomic, assign) BOOL historyExpanded; // 是否展开所有历史
|
||||||
@property (nonatomic, assign) CGFloat lastCollectionWidth; // 记录宽度变化,用于重新计算
|
@property (nonatomic, assign) CGFloat lastCollectionWidth; // 记录宽度变化,用于重新计算
|
||||||
|
@property (nonatomic, strong) KBSearchVM *viewModel;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation KBSearchVC
|
@implementation KBSearchVC
|
||||||
@@ -89,16 +92,10 @@ typedef NS_ENUM(NSInteger, KBSearchSection) {
|
|||||||
KBLocalized(@"水果萝卜"),
|
KBLocalized(@"水果萝卜"),
|
||||||
KBLocalized(@"熟冻帝王蟹"),
|
KBLocalized(@"熟冻帝王蟹"),
|
||||||
KBLocalized(@"赣南脐橙")] mutableCopy];
|
KBLocalized(@"赣南脐橙")] mutableCopy];
|
||||||
self.recommendItems = @[
|
self.recommendedThemes = @[];
|
||||||
@{@"title":@"Dopamine", @"price":@"20"},
|
|
||||||
@{@"title":@"Dopamine", @"price":@"20"},
|
|
||||||
@{@"title":@"Dopamine", @"price":@"20"},
|
|
||||||
@{@"title":@"Dopamine", @"price":@"20"},
|
|
||||||
@{@"title":@"Dopamine", @"price":@"20"},
|
|
||||||
@{@"title":@"Dopamine", @"price":@"20"},
|
|
||||||
];
|
|
||||||
|
|
||||||
[self.collectionView reloadData];
|
[self.collectionView reloadData];
|
||||||
|
[self fetchRecommendedThemes];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)viewDidLayoutSubviews {
|
- (void)viewDidLayoutSubviews {
|
||||||
@@ -252,7 +249,7 @@ typedef NS_ENUM(NSInteger, KBSearchSection) {
|
|||||||
NSArray *list = [self currentDisplayHistory];
|
NSArray *list = [self currentDisplayHistory];
|
||||||
return list.count; // 历史最多两行;可能包含“更多”占位
|
return list.count; // 历史最多两行;可能包含“更多”占位
|
||||||
}
|
}
|
||||||
return self.recommendItems.count;
|
return self.recommendedThemes.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||||
@@ -269,8 +266,10 @@ typedef NS_ENUM(NSInteger, KBSearchSection) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
KBSkinCardCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kSkinCellId forIndexPath:indexPath];
|
KBSkinCardCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kSkinCellId forIndexPath:indexPath];
|
||||||
NSDictionary *it = self.recommendItems[indexPath.item];
|
KBShopThemeModel *model = self.recommendedThemes[indexPath.item];
|
||||||
[cell configWithTitle:it[@"title"] imageURL:nil price:it[@"price"]];
|
[cell configWithTitle:model.themeName ?: @""
|
||||||
|
imageURL:model.themePreviewImageUrl
|
||||||
|
price:[self priceTextForTheme:model]];
|
||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,6 +397,28 @@ typedef NS_ENUM(NSInteger, KBSearchSection) {
|
|||||||
|
|
||||||
- (void)onTapBack { [self.navigationController popViewControllerAnimated:YES]; }
|
- (void)onTapBack { [self.navigationController popViewControllerAnimated:YES]; }
|
||||||
|
|
||||||
|
- (void)fetchRecommendedThemes {
|
||||||
|
__weak typeof(self) weakSelf = self;
|
||||||
|
[self.viewModel fetchRecommendedThemesWithCompletion:^(NSArray<KBShopThemeModel *> * _Nullable themes, NSError * _Nullable error) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
if (error) {
|
||||||
|
NSLog(@"[KBSearchVC] fetch recommended failed: %@", error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
weakSelf.recommendedThemes = themes ?: @[];
|
||||||
|
NSIndexSet *sections = [NSIndexSet indexSetWithIndex:KBSearchSectionRecommend];
|
||||||
|
[weakSelf.collectionView reloadSections:sections];
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)priceTextForTheme:(KBShopThemeModel *)model {
|
||||||
|
if (model.themePrice > 0.0) {
|
||||||
|
return [NSString stringWithFormat:@"%.2f", model.themePrice];
|
||||||
|
}
|
||||||
|
return @"0";
|
||||||
|
}
|
||||||
|
|
||||||
- (UICollectionViewLeftAlignedLayout *)flowLayout {
|
- (UICollectionViewLeftAlignedLayout *)flowLayout {
|
||||||
if (!_flowLayout) {
|
if (!_flowLayout) {
|
||||||
_flowLayout = [[UICollectionViewLeftAlignedLayout alloc] init];
|
_flowLayout = [[UICollectionViewLeftAlignedLayout alloc] init];
|
||||||
@@ -423,4 +444,11 @@ typedef NS_ENUM(NSInteger, KBSearchSection) {
|
|||||||
return _collectionView;
|
return _collectionView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (KBSearchVM *)viewModel {
|
||||||
|
if (!_viewModel) {
|
||||||
|
_viewModel = [[KBSearchVM alloc] init];
|
||||||
|
}
|
||||||
|
return _viewModel;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
32
keyBoard/Class/Search/VM/KBSearchVM.h
Normal file
32
keyBoard/Class/Search/VM/KBSearchVM.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
//
|
||||||
|
// KBSearchVM.h
|
||||||
|
// keyBoard
|
||||||
|
//
|
||||||
|
// Created by Mac on 2025/12/17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@class KBShopThemeModel;
|
||||||
|
@class KBSearchThemeModel;
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
typedef void(^KBSearchRecommendedCompletion)(NSArray<KBShopThemeModel *> *_Nullable themes,
|
||||||
|
NSError *_Nullable error);
|
||||||
|
typedef void(^KBSearchThemesCompletion)(NSArray<KBSearchThemeModel *> *_Nullable themes,
|
||||||
|
NSError *_Nullable error);
|
||||||
|
|
||||||
|
@interface KBSearchVM : NSObject
|
||||||
|
|
||||||
|
/// 推荐主题列表(复用 KBShopVM 的 API_THEME_RECOMMENDED)
|
||||||
|
- (void)fetchRecommendedThemesWithCompletion:(KBSearchRecommendedCompletion)completion;
|
||||||
|
|
||||||
|
/// 搜索主题:GET /themes/search?themeName=xxx
|
||||||
|
- (void)searchThemesWithName:(NSString *)themeName
|
||||||
|
completion:(KBSearchThemesCompletion)completion;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
||||||
77
keyBoard/Class/Search/VM/KBSearchVM.m
Normal file
77
keyBoard/Class/Search/VM/KBSearchVM.m
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
//
|
||||||
|
// KBSearchVM.m
|
||||||
|
// keyBoard
|
||||||
|
//
|
||||||
|
// Created by Mac on 2025/12/17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "KBSearchVM.h"
|
||||||
|
#import "KBShopVM.h"
|
||||||
|
#import "KBNetworkManager.h"
|
||||||
|
#import "KBAPI.h"
|
||||||
|
#import "KBBizCode.h"
|
||||||
|
#import "KBSearchThemeModel.h"
|
||||||
|
#import <MJExtension/MJExtension.h>
|
||||||
|
|
||||||
|
@interface KBSearchVM ()
|
||||||
|
@property (nonatomic, strong) KBShopVM *shopVM;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation KBSearchVM
|
||||||
|
|
||||||
|
- (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)fetchRecommendedThemesWithCompletion:(KBSearchRecommendedCompletion)completion {
|
||||||
|
[self.shopVM fetchRecommendedThemesWithCompletion:^(NSArray<KBShopThemeModel *> * _Nullable themes, NSError * _Nullable error) {
|
||||||
|
if (completion) completion(themes, error);
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)searchThemesWithName:(NSString *)themeName
|
||||||
|
completion:(KBSearchThemesCompletion)completion {
|
||||||
|
NSString *trim = [themeName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||||
|
if (trim.length == 0) {
|
||||||
|
if (completion) completion(nil, [self kb_invalidParameterError]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NSDictionary *params = @{@"themeName": trim};
|
||||||
|
[[KBNetworkManager shared] GET:API_THEME_SEARCH
|
||||||
|
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<KBSearchThemeModel *> *list = [KBSearchThemeModel mj_objectArrayWithKeyValuesArray:(NSArray *)dataObj];
|
||||||
|
if (completion) completion(list, nil);
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (KBShopVM *)shopVM {
|
||||||
|
if (!_shopVM) {
|
||||||
|
_shopVM = [[KBShopVM alloc] init];
|
||||||
|
}
|
||||||
|
return _shopVM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
Reference in New Issue
Block a user