// // MedalsLevelIndicatorView.m // YuMi // // Created by P on 2025/6/18. // #import "MedalsLevelIndicatorView.h" #import "UIImage+Utils.h" // 等级指示器视图 @interface LevelItemView : UIView @property (nonatomic, strong) UILabel *levelLabel; @property (nonatomic, strong) UIView *dotView; // 圆点视图 @property (nonatomic, strong) NetImageView *imageView; // 图片视图 @property (nonatomic, assign) BOOL isSelected; @property (nonatomic, assign) NSInteger level; @property (nonatomic, assign) BOOL hasImage; @property (nonatomic, strong) UIImage *originalImage; // 保存原始彩色图片 @property (nonatomic, strong) UIImage *grayImage; // 保存灰度图片 - (instancetype)initWithLevel:(NSInteger)level; - (void)setSelected:(BOOL)selected animated:(BOOL)animated; - (void)setImageUrl:(NSString *)imageUrl; @end @implementation LevelItemView - (instancetype)initWithLevel:(NSInteger)level { self = [super init]; if (self) { _level = level; _isSelected = NO; _hasImage = 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)setImageUrl:(NSString *)imageUrl { if (!_imageView) { // 创建图片视图 _imageView = [[NetImageView alloc] init]; _imageView.contentMode = UIViewContentModeScaleAspectFill; _imageView.layer.cornerRadius = 15; _imageView.clipsToBounds = YES; [self addSubview:_imageView]; [_imageView mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.mas_equalTo(self); make.bottom.mas_equalTo(_dotView.mas_top).offset(-4); make.width.height.mas_equalTo(30); }]; // 调整圆点位置 [_dotView mas_remakeConstraints:^(MASConstraintMaker *make) { make.centerX.mas_equalTo(self); make.top.mas_equalTo(_imageView.mas_bottom).offset(4); make.width.height.mas_equalTo(6); }]; } _hasImage = YES; // 调试时使用测试URL #ifdef DEBUG NSString *testUrl = @"https://img.toto.im/mw600/66b3de17ly1i2jopju47bj20xc1e0dx4.jpg.webp"; imageUrl = testUrl; NSLog(@"调试模式: 使用测试图片URL: %@", testUrl); #endif // 加载图片并处理灰度效果 @kWeakify(self); [_imageView loadImageWithUrl:imageUrl completion:^(UIImage * _Nullable image, NSURL * _Nonnull url) { @kStrongify(self); if (image) { self.originalImage = image; self.grayImage = [image grayscaleImage]; [self updateImageEffect]; } }]; } // 更新图片效果 - (void)updateImageEffect { if (!_hasImage || !_imageView) { return; } if (_isSelected && _originalImage) { // 选中状态:显示彩色图片 _imageView.image = _originalImage; } else if (!_isSelected && _grayImage) { // 非选中状态:显示灰度图片 _imageView.image = _grayImage; } } - (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 (self.hasImage) { [self updateImageEffect]; } }; if (animated) { [UIView animateWithDuration:0.2 animations:updateBlock]; } else { updateBlock(); } } @end // 等级指示器容器视图 @interface MedalsLevelIndicatorView() @property (nonatomic, strong) NSMutableArray *levelItems; @property (nonatomic, strong) NSMutableArray *connectionLines; // 连接线数组 @property (nonatomic, assign) NSInteger maxLevel; @property (nonatomic, assign) NSInteger selectedLevel; @end @implementation MedalsLevelIndicatorView - (instancetype)init { self = [super init]; if (self) { _levelItems = [NSMutableArray array]; _connectionLines = [NSMutableArray array]; _selectedLevel = 1; _indicatorType = MedalsLevelIndicatorTypeNormal; 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]; // 根据类型设置图片可见性 [self updateImageVisibility]; } - (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)setIndicatorType:(MedalsLevelIndicatorType)indicatorType { _indicatorType = indicatorType; // 如果已经创建了视图,需要根据类型进行调整 if (_levelItems.count > 0) { [self updateImageVisibility]; } } // 根据指示器类型更新图片可见性 - (void)updateImageVisibility { for (LevelItemView *item in _levelItems) { if (item.imageView) { item.imageView.hidden = (_indicatorType == MedalsLevelIndicatorTypeNormal); } } } - (void)setImageUrl:(NSString *)imageUrl forLevel:(NSInteger)level { if (level < 1 || level > _maxLevel || level > _levelItems.count) { return; } LevelItemView *item = _levelItems[level - 1]; [item setImageUrl:imageUrl]; // 根据当前类型设置图片可见性 if (item.imageView) { item.imageView.hidden = (_indicatorType == MedalsLevelIndicatorTypeNormal); } } - (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