Files
keyboard/keyBoard/Class/Me/VC/MySkinVC.m
2025-11-08 21:44:41 +08:00

257 lines
10 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.

//
// MySkinVC.m
// keyBoard
//
// 我的皮肤列表页(支持编辑多选删除)。
// 需求要点:
// - 顶部右侧 Editor/Cancel 切换
// - CollectionView Masonry 约束 + 懒加载控件
// - 自定义 cell编辑态显示左上角选择圆点支持多选
// - 底部 bottomView 展示已选择数量与删除按钮,数量>0 时 Delete 变为 #02BEAC 可点
//
#import "MySkinVC.h"
#import <Masonry/Masonry.h>
#import "UIColor+Extension.h"
#import "MySkinCell.h"
static NSString * const kMySkinCellId = @"kMySkinCellId";
@interface MySkinVC () <UICollectionViewDataSource, UICollectionViewDelegate>
@property (nonatomic, strong) UICollectionView *collectionView; // 列表
@property (nonatomic, strong) UIView *bottomView; // 底部操作条
@property (nonatomic, strong) UILabel *selectedLabel; // 已选择数量
@property (nonatomic, strong) UIButton *deleteButton; // 删除
@property (nonatomic, strong) NSMutableArray<NSDictionary *> *data; // 简单数据源
@property (nonatomic, assign, getter=isEditingMode) BOOL editingMode; // 是否编辑态
@end
@implementation MySkinVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.title = @"My Skin"; // 标题
// 右上角 Editor/Cancel
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Editor" style:UIBarButtonItemStylePlain target:self action:@selector(onToggleEdit)];
// 数据源(演示用)
self.data = [@[
@{ @"title": @"Dopamine" },
@{ @"title": @"Dopamine" },
@{ @"title": @"Dopamine" },
@{ @"title": @"Dopamine" },
] mutableCopy];
// 视图
[self.view addSubview:self.collectionView];
[self.view addSubview:self.bottomView];
[self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop);
make.left.right.equalTo(self.view);
make.bottom.equalTo(self.view.mas_bottom); // 底部被 bottomView 覆盖即可
}];
[self.bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.view);
make.bottom.equalTo(self.view.mas_safeAreaLayoutGuideBottom);
make.height.mas_equalTo(64);
}];
// 初始:非编辑态,隐藏底部
self.bottomView.hidden = YES;
}
#pragma mark - Actions
- (void)onToggleEdit {
self.editingMode = !self.editingMode;
// 更新顶部按钮
self.navigationItem.rightBarButtonItem.title = self.isEditingMode ? @"Cancel" : @"Editor";
// 控制底部栏显隐
self.bottomView.hidden = !self.isEditingMode;
// 列表进入/退出多选
self.collectionView.allowsMultipleSelection = self.isEditingMode;
// 直接刷新可见 cell确保切换后立刻出现/隐藏 markView
for (UICollectionViewCell *c in self.collectionView.visibleCells) {
if (![c isKindOfClass:MySkinCell.class]) continue;
MySkinCell *cell = (MySkinCell *)c;
cell.editing = self.isEditingMode;
NSIndexPath *ip = [self.collectionView indexPathForCell:cell];
BOOL selected = [[self.collectionView indexPathsForSelectedItems] containsObject:ip];
[cell updateSelected:selected];
}
// 再做一次 reload 保守刷新(防止 offscreen cell
[self.collectionView reloadData];
// 清空选择并刷新底部文案
for (NSIndexPath *ip in [self.collectionView indexPathsForSelectedItems]) {
[self.collectionView deselectItemAtIndexPath:ip animated:NO];
}
[self updateBottomUI];
}
- (void)onDelete {
// 根据选中项删除
NSArray<NSIndexPath *> *selected = [[self.collectionView indexPathsForSelectedItems] sortedArrayUsingSelector:@selector(compare:)];
if (selected.count == 0) return;
// 批量更新,先改数据,再删 UI
[self.collectionView performBatchUpdates:^{
NSMutableIndexSet *set = [NSMutableIndexSet indexSet];
for (NSIndexPath *ip in selected) { [set addIndex:ip.item]; }
[self.data removeObjectsAtIndexes:set];
[self.collectionView deleteItemsAtIndexPaths:selected];
} completion:^(BOOL finished) {
[self updateBottomUI];
}];
}
- (void)updateBottomUI {
NSInteger count = self.collectionView.indexPathsForSelectedItems.count;
self.selectedLabel.text = [NSString stringWithFormat:@"Selected: %ld Skins", (long)count];
BOOL enable = count > 0;
self.deleteButton.enabled = enable;
if (enable) {
self.deleteButton.backgroundColor = [UIColor colorWithHex:0x02BEAC];
[self.deleteButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
self.deleteButton.layer.borderColor = [UIColor colorWithHex:0x02BEAC].CGColor;
} else {
self.deleteButton.backgroundColor = [UIColor colorWithHex:0xF2F2F2];
[self.deleteButton setTitleColor:[UIColor colorWithHex:0xC8C8C8] forState:UIControlStateNormal];
self.deleteButton.layer.borderColor = [UIColor colorWithHex:0xE6E6E6].CGColor;
}
}
#pragma mark - UICollectionView
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.data.count;
}
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MySkinCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kMySkinCellId forIndexPath:indexPath];
NSDictionary *d = self.data[indexPath.item];
[cell configWithTitle:d[@"title"] image:nil];
cell.editing = self.isEditingMode; // 控制是否显示选择圆点
// 同步选中状态(复用时)
BOOL selected = [[collectionView indexPathsForSelectedItems] containsObject:indexPath];
[cell updateSelected:selected];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
if (!self.isEditingMode) {
// 非编辑态:可在此进入详情,当前示例不处理
[collectionView deselectItemAtIndexPath:indexPath animated:YES];
return;
}
MySkinCell *cell = (MySkinCell *)[collectionView cellForItemAtIndexPath:indexPath];
[cell updateSelected:YES];
[self updateBottomUI];
}
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {
if (!self.isEditingMode) return;
MySkinCell *cell = (MySkinCell *)[collectionView cellForItemAtIndexPath:indexPath];
[cell updateSelected:NO];
[self updateBottomUI];
}
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
if (![cell isKindOfClass:MySkinCell.class]) return;
MySkinCell *c = (MySkinCell *)cell;
c.editing = self.isEditingMode; // 保证滚动出现的新 cell 同步编辑态
BOOL selected = [[collectionView indexPathsForSelectedItems] containsObject:indexPath];
[c updateSelected:selected];
}
#pragma mark - Lazy
- (UICollectionView *)collectionView {
if (!_collectionView) {
UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new];
CGFloat inset = 16;
CGFloat spacing = 12;
CGFloat W = UIScreen.mainScreen.bounds.size.width;
CGFloat itemW = floor((W - inset * 2 - spacing) / 2.0);
CGFloat itemH = itemW * 0.82f; // 接近截图比例
layout.itemSize = CGSizeMake(itemW, itemH);
layout.minimumInteritemSpacing = spacing;
layout.minimumLineSpacing = spacing;
layout.sectionInset = UIEdgeInsetsMake(12, inset, 12, inset);
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
_collectionView.backgroundColor = [UIColor whiteColor];
_collectionView.dataSource = self;
_collectionView.delegate = self;
_collectionView.allowsMultipleSelection = NO; // 初始
[_collectionView registerClass:MySkinCell.class forCellWithReuseIdentifier:kMySkinCellId];
}
return _collectionView;
}
- (UIView *)bottomView {
if (!_bottomView) {
_bottomView = [UIView new];
_bottomView.backgroundColor = [UIColor whiteColor];
_bottomView.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.06].CGColor;
_bottomView.layer.shadowOpacity = 1;
_bottomView.layer.shadowOffset = CGSizeMake(0, -2);
[_bottomView addSubview:self.selectedLabel];
[_bottomView addSubview:self.deleteButton];
[self.selectedLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(_bottomView).offset(16);
make.centerY.equalTo(_bottomView);
}];
[self.deleteButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(_bottomView).offset(-16);
make.centerY.equalTo(_bottomView);
make.width.mas_equalTo(92);
make.height.mas_equalTo(36);
}];
}
return _bottomView;
}
- (UILabel *)selectedLabel {
if (!_selectedLabel) {
_selectedLabel = [UILabel new];
_selectedLabel.textColor = [UIColor colorWithHex:0x666666];
_selectedLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
_selectedLabel.text = @"Selected: 0 Skins";
}
return _selectedLabel;
}
- (UIButton *)deleteButton {
if (!_deleteButton) {
_deleteButton = [UIButton buttonWithType:UIButtonTypeSystem];
[_deleteButton setTitle:@"Delete" forState:UIControlStateNormal];
_deleteButton.titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold];
_deleteButton.layer.cornerRadius = 18;
_deleteButton.layer.borderWidth = 1;
_deleteButton.clipsToBounds = YES;
[_deleteButton addTarget:self action:@selector(onDelete) forControlEvents:UIControlEventTouchUpInside];
// 初始禁用态样式
_deleteButton.enabled = NO;
_deleteButton.backgroundColor = [UIColor colorWithHex:0xF2F2F2];
[_deleteButton setTitleColor:[UIColor colorWithHex:0xC8C8C8] forState:UIControlStateNormal];
_deleteButton.layer.borderColor = [UIColor colorWithHex:0xE6E6E6].CGColor;
}
return _deleteButton;
}
@end