This commit is contained in:
2025-11-08 22:25:57 +08:00
parent 41b14ceea4
commit 675a9f6d64
12 changed files with 544 additions and 4 deletions

View File

@@ -0,0 +1,19 @@
//
// KBSkinDetailHeaderCell.h
// keyBoard
//
// Created by Mac on 2025/11/8.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface KBSkinDetailHeaderCell : UICollectionViewCell
@property (nonatomic, strong) UIImageView *coverView; // 顶部大图
@property (nonatomic, strong) UILabel *leftLabel; // 下方左侧文案(#1B1F1A
@property (nonatomic, strong) UILabel *rightLabel; // 下方右侧文案(#02BEAC
- (void)configWithTitle:(NSString *)title right:(NSString *)right;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,75 @@
//
// KBSkinDetailHeaderCell.m
// keyBoard
//
// Created by Mac on 2025/11/8.
//
#import "KBSkinDetailHeaderCell.h"
@implementation KBSkinDetailHeaderCell
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.contentView.backgroundColor = [UIColor whiteColor];
self.contentView.layer.cornerRadius = 12;
self.contentView.layer.masksToBounds = YES;
[self.contentView addSubview:self.coverView];
[self.contentView addSubview:self.leftLabel];
[self.contentView addSubview:self.rightLabel];
// 16:9
[self.coverView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.right.equalTo(self.contentView);
//
make.height.equalTo(self.contentView.mas_width).multipliedBy(0.58);
}];
[self.leftLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.contentView).offset(12);
make.top.equalTo(self.coverView.mas_bottom).offset(10);
}];
[self.rightLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.contentView).offset(-12);
make.centerY.equalTo(self.leftLabel);
}];
}
return self;
}
- (void)configWithTitle:(NSString *)title right:(NSString *)right {
// /
self.leftLabel.text = title.length ? title : @"Dopamine";
self.rightLabel.text = right.length ? right : @"Download: 1 Million";
//
self.coverView.backgroundColor = [UIColor colorWithWhite:0.94 alpha:1.0];
}
#pragma mark - Lazy
- (UIImageView *)coverView {
if (!_coverView) {
_coverView = [[UIImageView alloc] init];
_coverView.contentMode = UIViewContentModeScaleAspectFill;
_coverView.clipsToBounds = YES;
}
return _coverView;
}
- (UILabel *)leftLabel {
if (!_leftLabel) {
_leftLabel = [UILabel new];
_leftLabel.textColor = [UIColor colorWithHex:0x1B1F1A];
_leftLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightSemibold];
_leftLabel.text = @"Dopamine";
}
return _leftLabel;
}
- (UILabel *)rightLabel {
if (!_rightLabel) {
_rightLabel = [UILabel new];
_rightLabel.textColor = [UIColor colorWithHex:0x02BEAC];
_rightLabel.font = [UIFont systemFontOfSize:15 weight:UIFontWeightMedium];
_rightLabel.textAlignment = NSTextAlignmentRight;
_rightLabel.text = @"Download: 1 Million";
}
return _rightLabel;
}
@end

View File

@@ -0,0 +1,18 @@
//
// KBSkinDetailTagCell.h
// keyBoard
//
// Created by Mac on 2025/11/8.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface KBSkinDetailTagCell : UICollectionViewCell
- (void)config:(NSString *)text;
/// 根据文案计算自适应宽度(外部布局用)
+ (CGSize)sizeForText:(NSString *)text;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,48 @@
//
// KBSkinDetailTagCell.m
// keyBoard
//
// Created by Mac on 2025/11/8.
//
#import "KBSkinDetailTagCell.h"
@interface KBSkinDetailTagCell ()
@property (nonatomic, strong) UILabel *titleLabel;
@end
@implementation KBSkinDetailTagCell
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.contentView.backgroundColor = [UIColor colorWithWhite:0.96 alpha:1.0];
self.contentView.layer.cornerRadius = 16;
self.contentView.layer.masksToBounds = YES;
[self.contentView addSubview:self.titleLabel];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.contentView).insets(UIEdgeInsetsMake(6, 12, 6, 12));
}];
}
return self;
}
- (void)config:(NSString *)text {
self.titleLabel.text = text ?: @"";
}
+ (CGSize)sizeForText:(NSString *)text {
if (text.length == 0) { return CGSizeMake(40, 32); }
CGSize s = [text sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:14]}];
// 12 + 12 32
return CGSizeMake(ceil(s.width) + 24, 32);
}
#pragma mark - Lazy
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] init];
_titleLabel.font = [UIFont systemFontOfSize:14];
_titleLabel.textColor = [UIColor colorWithHex:0x1B1F1A];
}
return _titleLabel;
}
@end

View File

@@ -0,0 +1,17 @@
//
// KBSkinSectionTitleCell.h
// keyBoard
//
// Created by Mac on 2025/11/8.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/// MARK: - 区块标题 cell
@interface KBSkinSectionTitleCell : UICollectionViewCell
@property (nonatomic, strong) UILabel *titleLabel;
- (void)config:(NSString *)title;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,36 @@
//
// KBSkinSectionTitleCell.m
// keyBoard
//
// Created by Mac on 2025/11/8.
//
#import "KBSkinSectionTitleCell.h"
@implementation KBSkinSectionTitleCell
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.contentView.backgroundColor = [UIColor whiteColor];
[self.contentView addSubview:self.titleLabel];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.contentView).offset(16);
make.centerY.equalTo(self.contentView);
}];
}
return self;
}
- (void)config:(NSString *)title {
self.titleLabel.text = title ?: @"Recommended Skin";
}
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [UILabel new];
_titleLabel.textColor = [UIColor colorWithHex:0x1B1F1A];
_titleLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightSemibold];
_titleLabel.text = @"Recommended Skin";
}
return _titleLabel;
}
@end

View File

@@ -0,0 +1,20 @@
//
// KBSkinTagsContainerCell.h
// keyBoard
//
// Created by Mac on 2025/11/8.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface KBSkinTagsContainerCell : UICollectionViewCell <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
@property (nonatomic, strong) UICollectionView *tagsView; // 内部标签列表
@property (nonatomic, copy) NSArray<NSString *> *tags; // 标签文案
- (void)configWithTags:(NSArray<NSString *> *)tags;
/// 根据给定宽度,计算该容器需要的高度(用于外部 sizeForItem
+ (CGFloat)heightForTags:(NSArray<NSString *> *)tags width:(CGFloat)width;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,91 @@
//
// KBSkinTagsContainerCell.m
// keyBoard
//
// Created by Mac on 2025/11/8.
//
#import "KBSkinTagsContainerCell.h"
#import "KBSkinDetailTagCell.h"
#import "UICollectionViewLeftAlignedLayout.h"
static NSString * const kInnerTagCellId = @"kInnerTagCellId";
@implementation KBSkinTagsContainerCell
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.contentView.backgroundColor = [UIColor whiteColor];
[self.contentView addSubview:self.tagsView];
[self.tagsView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.contentView);
}];
}
return self;
}
- (void)configWithTags:(NSArray<NSString *> *)tags {
self.tags = tags;
[self.tagsView reloadData];
}
#pragma mark - UICollectionView
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.tags.count;
}
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
KBSkinDetailTagCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kInnerTagCellId forIndexPath:indexPath];
[cell config:self.tags[indexPath.item]];
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
// 32
return [KBSkinDetailTagCell sizeForText:self.tags[indexPath.item]];
}
#pragma mark - Height Helper
+ (CGFloat)heightForTags:(NSArray<NSString *> *)tags width:(CGFloat)width {
if (tags.count == 0) { return 0; }
// 8item 8sectionInsets = {0,16,0,16}
CGFloat leftRight = 16 * 2; // VC sectionInset=16
CGFloat maxWidth = width - leftRight;
CGFloat x = 0;
CGFloat rows = 1;
for (NSString *t in tags) {
CGSize s = [KBSkinDetailTagCell sizeForText:t];
CGFloat iw = ceil(s.width);
if (x == 0) {
x = iw;
} else {
// item 8
if (x + 8 + iw > maxWidth) { //
rows += 1;
x = iw;
} else {
x += 8 + iw;
}
}
}
// 32 8 8
return rows * 32 + (rows - 1) * 8 + 16;
}
#pragma mark - Lazy
- (UICollectionView *)tagsView {
if (!_tagsView) {
UICollectionViewLeftAlignedLayout *layout = [UICollectionViewLeftAlignedLayout new];
layout.minimumInteritemSpacing = 8;
layout.minimumLineSpacing = 8;
layout.sectionInset = UIEdgeInsetsMake(8, 0, 8, 16);
_tagsView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
_tagsView.backgroundColor = [UIColor whiteColor];
_tagsView.dataSource = self;
_tagsView.delegate = self;
[_tagsView registerClass:KBSkinDetailTagCell.class forCellWithReuseIdentifier:kInnerTagCellId];
_tagsView.scrollEnabled = NO; //
}
return _tagsView;
}
@end