Files
peko-ios/YuMi/Modules/YMMine/View/Medals/MedalsWearingListCollectionViewCell.m
edwinQQQ 99db078b62 feat(勋章): 新增多等级高亮功能并优化勋章展示逻辑
refactor(勋章排行): 重构排行榜分页加载和刷新逻辑

fix(会话): 优化官方账号判断逻辑和跳转处理

style(UI): 调整勋章排行榜和游戏菜单UI布局

chore: 更新Podfile配置和bitcode框架列表
2025-07-03 19:35:17 +08:00

338 lines
9.8 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.

//
// MedalsWearingListCollectionViewCell.m
// YuMi
//
// Created by P on 2025/6/19.
//
#import "MedalsWearingListCollectionViewCell.h"
#import <QGVAPWrapView.h>
#import "XPRoomGiftAnimationParser.h"
#import "MedalsModel.h"
@interface MedalsWearingListCollectionViewCell ()
@property(nonatomic, copy) NSString *imagePath;
@property(nonatomic, copy) NSString *mp4Path;
@property(nonatomic, strong) NetImageView *imageView;
@property(nonatomic, strong) VAPView *mp4View;
@property(nonatomic, strong) XPRoomGiftAnimationParser *mp4Parser;
@property (nonatomic, strong) UIImageView *selectedImageView;
@property (nonatomic, assign) BOOL isVisible; // 跟踪 cell 是否可见
// 新增属性:用于降级处理
@property (nonatomic, strong) MedalVo *currentMedalModel; // 当前显示的 medal 模型
@end
@implementation MedalsWearingListCollectionViewCell
+ (NSString *)cellID {
return NSStringFromClass([MedalsWearingListCollectionViewCell class]);
}
+ (void)registerTo:(UICollectionView *)collectionView {
[collectionView registerClass:[self class] forCellWithReuseIdentifier:[self cellID]];
}
+ (instancetype)cellFor:(UICollectionView *)collectionView atIndexPath:(NSIndexPath *)index {
MedalsWearingListCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:[self cellID]
forIndexPath:index];
cell.isVisible = YES;
return cell;
}
- (void)updateCell:(MedalVo *)medalModel {
self.selectedImageView.hidden = medalModel.useStatus==NO;
// 保存当前模型,用于降级处理
self.currentMedalModel = medalModel;
// 按照新的显示逻辑处理
[self handleMedalDisplay:medalModel];
}
#pragma mark - 新的显示逻辑
- (void)handleMedalDisplay:(MedalVo *)medalModel {
if (!medalModel) {
[self setImagePath:@""];
return;
}
// 使用 picUrl
if (![NSString isEmpty:medalModel.picUrl]) {
// 3. 使用 picUrl 时,判断内容是否为图片
if ([NSString isImageFormat:medalModel.picUrl]) {
[self setImagePath:medalModel.picUrl];
} else {
// 降级
[self handleLegacyDisplay:medalModel];
}
return;
} else if (![NSString isEmpty:medalModel.mp4Url]) {
// 判断 mp4Url 是否有内容
[self setMp4Path:medalModel.mp4Url];
return;
}
// 4. 都为空,显示默认占位图
[self setImagePath:@""];
}
#pragma mark - mp4 失败降级处理
- (void)handleMp4FailureFallback {
if (!self.currentMedalModel) {
[self setImagePath:@""];
return;
}
// mp4 下载失败,降级使用 picUrl
if (![NSString isEmpty:self.currentMedalModel.picUrl]) {
if ([NSString isImageFormat:self.currentMedalModel.picUrl]) {
[self setImagePath:self.currentMedalModel.picUrl];
} else {
// picUrl 也不是图片格式,显示默认占位图
[self setImagePath:@""];
}
} else {
// picUrl 为空,显示默认占位图
[self setImagePath:@""];
}
}
#pragma mark - 旧版显示逻辑(用于 Debug 环境兼容)
- (void)handleLegacyDisplay:(MedalVo *)medalModel {
if ([medalModel.picUrl hasSuffix:@"mp4"]) {
[self setMp4Path:medalModel.picUrl];
} else {
[self setImagePath:medalModel.picUrl];
}
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setupUI];
[self setupNotifications];
}
return self;
}
- (void)setupUI {
[self.contentView addGradientBackgroundWithColors:@[
UIColorFromRGB(0x41007b),
UIColorFromRGB(0x290858)
] startPoint:CGPointMake(0.5, 0) endPoint:CGPointMake(0.5, 1) cornerRadius:8];
[self.contentView setAllCornerRadius:8
borderWidth:1
borderColor:UIColorFromRGB(0xa166bf)];
self.imageView = [[NetImageView alloc] init];
self.imageView.contentMode = UIViewContentModeScaleAspectFill;
[self.contentView addSubview:self.imageView];
[self.imageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.mas_equalTo(self.contentView);
make.top.mas_equalTo(13);
make.leading.trailing.mas_equalTo(self.contentView).inset(13);
make.height.mas_equalTo(self.imageView.mas_width);
}];
[self.contentView addSubview:self.mp4View];
[self.mp4View mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self.imageView);
}];
[self.contentView addSubview:self.selectedImageView];
[self.selectedImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(7);
make.trailing.mas_equalTo(-7);
make.size.mas_equalTo(CGSizeMake(21, 21));
}];
}
- (void)setImagePath:(NSString *)imagePath {
// 停止之前的 mp4 播放
[self stopMP4Playback];
_imagePath = imagePath;
self.mp4View.hidden = YES;
self.imageView.hidden = NO;
self.imageView.imageUrl = imagePath;
}
- (void)setMp4Path:(NSString *)mp4Path {
if ([NSString isEmpty:mp4Path]) {
return;
}
// 如果是相同的 mp4 路径,不需要重新加载
// if ([_mp4Path isEqualToString:mp4Path]) {
// return;
// }
// 停止之前的 mp4 播放
[self stopMP4Playback];
_mp4Path = mp4Path;
self.mp4View.hidden = NO;
self.imageView.hidden = YES;
if (!_mp4Parser) {
self.mp4Parser = [[XPRoomGiftAnimationParser alloc] init];
}
@kWeakify(self);
[self.mp4Parser parseWithURL:mp4Path
completionBlock:^(NSString * _Nullable videoUrl) {
@kStrongify(self);
if (![NSString isEmpty:videoUrl]) {
// 只有当 cell 可见时才播放
if (self.isVisible) {
[self.mp4View playHWDMP4:videoUrl repeatCount:-1 delegate:nil];
} else {
// 存储 URL但不立即播放
self.mp4View.tag = 1; // 标记已准备好播放
}
}
} failureBlock:^(NSError * _Nullable error) {
NSLog(@"Failed to parse mp4: %@", error);
// mp4 下载失败时,降级使用 picUrl
[self handleMp4FailureFallback];
}];
}
#pragma mark - MP4 播放控制
- (void)stopMP4Playback {
if (self.mp4View) {
[self.mp4View stopHWDMP4];
self.mp4View.tag = 0; // 重置播放状态标记
}
}
- (void)pauseMP4Playback {
if (self.mp4View && !self.mp4View.hidden) {
[self.mp4View pauseHWDMP4];
}
}
- (void)resumeMP4Playback {
if (self.mp4View && !self.mp4View.hidden && self.mp4Path) {
if (self.mp4View.tag == 1) { // 已准备好但尚未播放
@kWeakify(self);
[self.mp4Parser parseWithURL:self.mp4Path
completionBlock:^(NSString * _Nullable videoUrl) {
@kStrongify(self);
if (![NSString isEmpty:videoUrl] && self.isVisible) {
[self.mp4View playHWDMP4:videoUrl repeatCount:-1 delegate:nil];
}
} failureBlock:nil];
} else {
[self.mp4View resumeHWDMP4];
}
}
}
#pragma mark - 可见性管理
- (void)willDisplay {
self.isVisible = YES;
[self resumeMP4Playback];
}
- (void)didEndDisplaying {
self.isVisible = NO;
[self pauseMP4Playback];
}
#pragma mark - 通知处理
- (void)appDidEnterBackground {
[self pauseMP4Playback];
}
- (void)appWillEnterForeground {
if (self.isVisible) {
[self resumeMP4Playback];
}
}
- (void)didReceiveMemoryWarning {
// 内存警告时停止播放
if (!self.isVisible) {
[self stopMP4Playback];
}
}
#pragma mark - 生命周期
- (void)dealloc {
// 停止播放
[self stopMP4Playback];
// 移除通知观察者
[[NSNotificationCenter defaultCenter] removeObserver:self];
// 清理资源
self.mp4Parser = nil;
NSLog(@"MedalsWearingListCollectionViewCell dealloc");
}
#pragma mark -
- (VAPView *)mp4View {
if (!_mp4View) {
_mp4View = [[VAPView alloc] init];
_mp4View.contentMode = UIViewContentModeScaleAspectFit;
}
return _mp4View;
}
- (UIImageView *)selectedImageView {
if (!_selectedImageView) {
_selectedImageView = [[UIImageView alloc] initWithImage:kImage(@"medals_selected")];
_selectedImageView.hidden = YES;
}
return _selectedImageView;
}
- (void)setupNotifications {
// 监听应用进入后台和恢复前台的通知
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(appDidEnterBackground)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(appWillEnterForeground)
name:UIApplicationWillEnterForegroundNotification
object:nil];
// 监听内存警告通知
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didReceiveMemoryWarning)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
}
- (void)prepareForReuse {
[super prepareForReuse];
// 停止播放
[self stopMP4Playback];
// 隐藏 mp4 视图
self.mp4View.hidden = YES;
self.imageView.hidden = NO;
// 重置状态
self.mp4Path = nil;
self.imagePath = nil;
self.isVisible = NO;
self.currentMedalModel = nil; // 重置当前模型
}
@end