新增等级指示器视图和容器视图,更新 MedalsCollectionViewCell 以集成等级指示器功能,支持等级选择回调。更新 .vscode/settings.json 文件以包含新组件 QGVAP 的配置,保持代码结构一致性。

This commit is contained in:
edwinQQQ
2025-06-18 17:11:13 +08:00
parent 89d9b57dea
commit 413b2c6944
2 changed files with 274 additions and 2 deletions

View File

@@ -16,6 +16,7 @@
"Nonnull",
"NSEC",
"Procotol",
"QGVAP",
"Subview",
"Superview",
"Uids"

View File

@@ -10,6 +10,241 @@
#import <QGVAPWrapView.h>
#import "XPRoomGiftAnimationParser.h"
//
@interface LevelItemView : UIView
@property (nonatomic, strong) UILabel *levelLabel;
@property (nonatomic, strong) UIView *dotView; //
@property (nonatomic, assign) BOOL isSelected;
@property (nonatomic, assign) NSInteger level;
- (instancetype)initWithLevel:(NSInteger)level;
- (void)setSelected:(BOOL)selected animated:(BOOL)animated;
@end
@implementation LevelItemView
- (instancetype)initWithLevel:(NSInteger)level {
self = [super init];
if (self) {
_level = level;
_isSelected = NO;
//
_dotView = [[UIView alloc] init];
_dotView.backgroundColor = UIColorFromRGB(0x8B54E8);
_dotView.layer.cornerRadius = 3; //
_dotView.clipsToBounds = YES;
[self addSubview:_dotView];
//
self.levelLabel = [UILabel labelInitWithText:[NSString stringWithFormat:@"LV%ld", (long)level]
font:kFontMedium(10)
textColor:UIColorFromRGB(0x8B54E8)];
self.levelLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:self.levelLabel];
//
[_dotView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.mas_equalTo(self);
make.top.mas_equalTo(self);
make.width.height.mas_equalTo(6); //
}];
[self.levelLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.mas_equalTo(self);
make.top.mas_equalTo(_dotView.mas_bottom).offset(4); //
make.leading.trailing.bottom.mas_equalTo(self);
}];
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
if (_isSelected == selected) {
return;
}
_isSelected = selected;
void (^updateBlock)(void) = ^{
UIColor *color = selected ? [UIColor whiteColor] : UIColorFromRGB(0x8B54E8);
self.levelLabel.textColor = color;
self.dotView.backgroundColor = color;
};
if (animated) {
[UIView animateWithDuration:0.2 animations:updateBlock];
} else {
updateBlock();
}
}
@end
//
@interface LevelIndicatorView : UIView
@property (nonatomic, strong) NSMutableArray<LevelItemView *> *levelItems;
@property (nonatomic, strong) NSMutableArray<UIView *> *connectionLines; // 线
@property (nonatomic, assign) NSInteger maxLevel;
@property (nonatomic, assign) NSInteger selectedLevel;
@property (nonatomic, copy) void (^levelSelectedBlock)(NSInteger level);
@end
@implementation LevelIndicatorView
- (instancetype)init {
self = [super init];
if (self) {
_levelItems = [NSMutableArray array];
_connectionLines = [NSMutableArray array];
_selectedLevel = 1;
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
[self addGestureRecognizer:tapGesture];
}
return self;
}
- (void)setupWithMaxLevel:(NSInteger)maxLevel {
if (maxLevel < 0) {
maxLevel = 1;
}
if (_maxLevel == maxLevel && _levelItems.count == maxLevel) {
return;
}
_maxLevel = maxLevel;
// 线
for (UIView *view in _levelItems) {
[view removeFromSuperview];
}
[_levelItems removeAllObjects];
for (UIView *line in _connectionLines) {
[line removeFromSuperview];
}
[_connectionLines removeAllObjects];
//
CGFloat itemWidth = 25.0; //
CGFloat spacing = 15.0; //
CGFloat totalWidth = itemWidth * maxLevel + spacing * (maxLevel - 1);
CGFloat startX = (self.bounds.size.width - totalWidth) / 2;
for (NSInteger i = 1; i <= maxLevel; i++) {
//
LevelItemView *levelItem = [[LevelItemView alloc] initWithLevel:i];
[self addSubview:levelItem];
[_levelItems addObject:levelItem];
//
CGFloat x = startX + (i - 1) * (itemWidth + spacing);
[levelItem mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(itemWidth);
make.height.mas_equalTo(self);
make.centerY.mas_equalTo(self);
make.leading.mas_equalTo(self).offset(x);
}];
// 线
if (i > 1) {
UIView *line = [[UIView alloc] init];
line.backgroundColor = UIColorFromRGB(0x8B54E8); //
[self insertSubview:line atIndex:0];
[_connectionLines addObject:line];
// 线
[line mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(1); // 线
make.centerY.mas_equalTo(levelItem.dotView);
make.leading.mas_equalTo(_levelItems[i-2].dotView.mas_centerX);
make.trailing.mas_equalTo(levelItem.dotView.mas_centerX);
}];
}
}
// LV1
[self setSelectedLevel:1 animated:NO];
}
- (void)layoutSubviews {
[super layoutSubviews];
if (_maxLevel > 0 && _levelItems.count > 0) {
//
CGFloat itemWidth = 25.0;
CGFloat spacing = 15.0;
CGFloat totalWidth = itemWidth * _maxLevel + spacing * (_maxLevel - 1);
CGFloat startX = (self.bounds.size.width - totalWidth) / 2;
for (NSInteger i = 0; i < _levelItems.count; i++) {
LevelItemView *item = _levelItems[i];
CGFloat x = startX + i * (itemWidth + spacing);
[item mas_updateConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(self).offset(x);
}];
}
}
}
- (void)setSelectedLevel:(NSInteger)level animated:(BOOL)animated {
if (level < 1 || level > _maxLevel) {
return;
}
_selectedLevel = level;
//
for (NSInteger i = 0; i < _levelItems.count; i++) {
LevelItemView *item = _levelItems[i];
[item setSelected:(i + 1) <= level animated:animated];
}
// 线
for (NSInteger i = 0; i < _connectionLines.count; i++) {
UIView *line = _connectionLines[i];
// 线
// 线线
BOOL isSelected = (i + 2) <= level;
void (^updateBlock)(void) = ^{
line.backgroundColor = isSelected ? [UIColor whiteColor] : UIColorFromRGB(0x8B54E8);
};
if (animated) {
[UIView animateWithDuration:0.2 animations:updateBlock];
} else {
updateBlock();
}
}
}
- (void)handleTap:(UITapGestureRecognizer *)gesture {
CGPoint location = [gesture locationInView:self];
for (NSInteger i = 0; i < _levelItems.count; i++) {
LevelItemView *item = _levelItems[i];
CGPoint itemLocation = [self convertPoint:location toView:item];
if (CGRectContainsPoint(item.bounds, itemLocation)) {
NSInteger level = i + 1;
[self setSelectedLevel:level animated:YES];
if (_levelSelectedBlock) {
_levelSelectedBlock(level);
}
break;
}
}
}
@end
@interface MedalsCollectionViewCell ()
@property(nonatomic, copy) NSString *imagePath;
@@ -20,8 +255,10 @@
@property(nonatomic, strong) XPRoomGiftAnimationParser *mp4Parser;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UILabel *subLabel;
@property (nonatomic, strong) LevelIndicatorView *levelIndicatorView;
@property (nonatomic, strong) MedalVos *displayModel;
@property (nonatomic, strong) MedalSeriesItemVo *currentItemVo;
@end
@implementation MedalsCollectionViewCell
@@ -84,13 +321,48 @@
make.top.mas_equalTo(self.titleLabel.mas_bottom).offset(7);
make.leading.trailing.mas_equalTo(self.contentView).inset(3);
}];
//
self.levelIndicatorView = [[LevelIndicatorView alloc] init];
[self.contentView addSubview:self.levelIndicatorView];
[self.levelIndicatorView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.mas_equalTo(self.contentView);
make.bottom.mas_equalTo(self.contentView).offset(-8);
make.left.right.mas_greaterThanOrEqualTo(self.contentView).inset(8);
make.height.mas_equalTo(26); //
}];
//
@kWeakify(self);
self.levelIndicatorView.levelSelectedBlock = ^(NSInteger level) {
@kStrongify(self);
//
if (self.currentItemVo && level <= self.currentItemVo.medalVos.count) {
MedalVos *selectedMedalVo = [self.currentItemVo.medalVos xpSafeObjectAtIndex:level - 1];
if (selectedMedalVo) {
self.displayModel = selectedMedalVo;
[self updateDisplayWithCurrentModel];
}
}
};
}
return self;
}
- (void)updateCell:(MedalSeriesVo *)model {
MedalSeriesItemVo *itemVos = [model.medalSeries xpSafeObjectAtIndex:0];
self.currentItemVo = itemVos;
self.displayModel = [itemVos.medalVos xpSafeObjectAtIndex:0];
//
[self.levelIndicatorView setupWithMaxLevel:itemVos.medalLevel];
[self.levelIndicatorView setSelectedLevel:1 animated:NO];
[self updateDisplayWithCurrentModel];
}
- (void)updateDisplayWithCurrentModel {
if (self.displayModel) {
if ([self.displayModel.picUrl hasSuffix:@"mp4"]) {
[self setMp4Path:self.displayModel.picUrl];
@@ -109,7 +381,6 @@
[NSDate timestampSwitchTime:self.displayModel.expireSeconds
formatter:@"yyyy/mm/dd"]];
}
}
}