Files
peko-ios/YuMi/Modules/YMMine/View/Medals/MedalsWearingListCollectionViewCell.m

354 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.

//
// 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;
}
#ifdef DEBUG
// Debug 环境:如果数据不满足新逻辑条件,使用现有逻辑
if ([NSString isEmpty:medalModel.mp4Url] && [NSString isEmpty:medalModel.picUrl]) {
// 都为空,使用旧逻辑
[self handleLegacyDisplay:medalModel];
return;
}
if (![NSString isEmpty:medalModel.picUrl] && [NSString isEmpty:medalModel.mp4Url]) {
// 只有 picUrl 有值mp4Url 为空,检查是否符合新逻辑
if (![NSString isImageFormat:medalModel.picUrl] && ![medalModel.picUrl.lowercaseString hasSuffix:@".mp4"]) {
// picUrl 既不是图片也不是 mp4使用旧逻辑
[self handleLegacyDisplay:medalModel];
return;
}
}
#endif
// 1. 优先判断 mp4Url 是否有内容
if (![NSString isEmpty:medalModel.mp4Url]) {
[self setMp4Path:medalModel.mp4Url];
return;
}
// 2. mp4Url 为空,使用 picUrl
if (![NSString isEmpty:medalModel.picUrl]) {
// 3. 使用 picUrl 时,判断内容是否为图片
if ([NSString isImageFormat:medalModel.picUrl]) {
[self setImagePath:medalModel.picUrl];
} else {
// 不是图片格式,显示默认占位图
[self setImagePath:@""];
}
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 {
// 如果是相同的 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