Files
keyboard/keyBoard/Class/Search/VC/KBSearchVC.m
2025-11-07 22:22:41 +08:00

219 lines
8.5 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// KBSearchVC.m
// keyBoard
//
// Created by Mac on 2025/11/7.
//
#import "KBSearchVC.h"
#import "KBSearchBarView.h"
#import "KBSearchSectionHeader.h"
#import "KBTagCell.h"
#import "KBSkinCardCell.h"
static NSString * const kTagCellId = @"KBTagCell";
static NSString * const kSkinCellId = @"KBSkinCardCell";
static NSString * const kHeaderId = @"KBSearchSectionHeader";
typedef NS_ENUM(NSInteger, KBSearchSection) {
KBSearchSectionHistory = 0,
KBSearchSectionRecommend = 1,
};
@interface KBSearchVC ()<UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
// 顶部搜索栏(封装的 View
@property (nonatomic, strong) KBSearchBarView *searchBarView;
// 列表
@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, strong) UICollectionViewFlowLayout *flowLayout;
// 数据
@property (nonatomic, strong) NSMutableArray<NSString *> *historyWords; // 历史搜索
@property (nonatomic, strong) NSArray<NSDictionary *> *recommendItems; // 推荐数据title/price
@end
@implementation KBSearchVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
// UI
[self.view addSubview:self.searchBarView];
[self.view addSubview:self.collectionView];
// 布局 - Masonry
[self.searchBarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_top).offset(KB_NAV_TOTAL_HEIGHT + 8);
make.left.equalTo(self.view).offset(16);
make.right.equalTo(self.view).offset(-16);
make.height.mas_equalTo(40);
}];
[self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.searchBarView.mas_bottom).offset(12);
make.left.right.bottom.equalTo(self.view);
}];
// 初始化测试数据
self.historyWords = [@[@"果冻橙", @"芒果", @"有机水果卷心菜", @"水果萝卜", @"熟冻帝王蟹", @"赣南脐橙"] mutableCopy];
self.recommendItems = @[
@{@"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];
}
#pragma mark - Private
/// 执行搜索:简单将关键字加入历史
- (void)performSearch:(NSString *)kw {
NSString *trim = [kw stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if (trim.length == 0) { return; }
// 去重插入到最前
[self.historyWords removeObject:trim];
[self.historyWords insertObject:trim atIndex:0];
[self.collectionView reloadData];
}
/// 清空历史
- (void)clearHistory {
[self.historyWords removeAllObjects];
[self.collectionView reloadData];
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 2; // 历史 + 推荐
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
if (section == KBSearchSectionHistory) {
return self.historyWords.count; // 无历史则为 0
}
return self.recommendItems.count;
}
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == KBSearchSectionHistory) {
KBTagCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kTagCellId forIndexPath:indexPath];
[cell config:self.historyWords[indexPath.item]];
return cell;
}
KBSkinCardCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kSkinCellId forIndexPath:indexPath];
NSDictionary *it = self.recommendItems[indexPath.item];
[cell configWithTitle:it[@"title"] imageURL:nil price:it[@"price"]];
return cell;
}
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
if (kind == UICollectionElementKindSectionHeader) {
KBSearchSectionHeader *header = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:kHeaderId forIndexPath:indexPath];
if (indexPath.section == KBSearchSectionHistory) {
// 当没有历史时,外部通过 sizeForHeader 返回 0这里仍设置但不会显示
[header configWithTitle:@"Historical Search" showTrash:self.historyWords.count > 0];
__weak typeof(self) weakSelf = self;
header.onTapTrash = ^{ [weakSelf clearHistory]; };
} else {
[header configWithTitle:@"Recommended Skin" showTrash:NO];
}
return header;
}
return [UICollectionReusableView new];
}
#pragma mark - UICollectionViewDelegateFlowLayout
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGFloat width = collectionView.bounds.size.width;
if (indexPath.section == KBSearchSectionHistory) {
NSString *t = self.historyWords[indexPath.item];
return [KBTagCell sizeForText:t];
}
// 两列卡片
CGFloat inset = 16; // 左右间距
CGFloat spacing = 12; // 列间距
CGFloat w = floor((width - inset*2 - spacing) / 2.0);
// 高度:封面 0.75w + 标题 + 价格 + 边距,近似定高
CGFloat h = w*0.75 + 8 + 20 + 10 + 6 + 8; // 估算
return CGSizeMake(w, h);
}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
if (section == KBSearchSectionHistory) {
return UIEdgeInsetsMake(8, 16, 12, 16);
}
return UIEdgeInsetsMake(8, 16, 20, 16);
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
return section == KBSearchSectionHistory ? 8 : 12;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
return section == KBSearchSectionHistory ? 8 : 16;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
// 当历史记录为空时header 高度为 0从而不显示标题与垃圾桶
if (section == KBSearchSectionHistory) {
return self.historyWords.count == 0 ? CGSizeZero : CGSizeMake(collectionView.bounds.size.width, 40);
}
return CGSizeMake(collectionView.bounds.size.width, 40);
}
#pragma mark - UICollectionViewDelegate
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == KBSearchSectionHistory) {
NSString *kw = self.historyWords[indexPath.item];
[self.searchBarView updateKeyword:kw];
[self performSearch:kw];
}
}
#pragma mark - Lazy
- (KBSearchBarView *)searchBarView {
if (!_searchBarView) {
_searchBarView = [[KBSearchBarView alloc] init];
_searchBarView.placeholder = @"Themes";
__weak typeof(self) weakSelf = self;
_searchBarView.onSearch = ^(NSString * _Nonnull keyword) {
[weakSelf performSearch:keyword];
};
}
return _searchBarView;
}
- (UICollectionViewFlowLayout *)flowLayout {
if (!_flowLayout) {
_flowLayout = [[UICollectionViewFlowLayout alloc] init];
_flowLayout.scrollDirection = UICollectionViewScrollDirectionVertical;
_flowLayout.sectionHeadersPinToVisibleBounds = NO;
}
return _flowLayout;
}
- (UICollectionView *)collectionView {
if (!_collectionView) {
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:self.flowLayout];
_collectionView.backgroundColor = [UIColor whiteColor];
_collectionView.dataSource = self;
_collectionView.delegate = self;
// 注册 cell & header
[_collectionView registerClass:KBTagCell.class forCellWithReuseIdentifier:kTagCellId];
[_collectionView registerClass:KBSkinCardCell.class forCellWithReuseIdentifier:kSkinCellId];
[_collectionView registerClass:KBSearchSectionHeader.class forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:kHeaderId];
}
return _collectionView;
}
@end