更新 MedalsModel.h 和 MedalsModel.m,新增 expireDateString 方法以格式化过期时间;在 MedalsCollectionViewCell.m 和 MedalsCyclePagerCell.m 中优化 MP4 和图片的显示逻辑,增强降级处理;在 MedalsDetailView.m 中更新勋章显示逻辑,使用新的 URL 优先级;在 MedalsLevelIndicatorView.h 和 MedalsLevelIndicatorView.m 中新增重置等级指示器的方法;在 MedalsWearingControlCollectionViewCell.m 和 MedalsWearingListCollectionViewCell.m 中实现新的显示逻辑,支持 MP4 和图片的降级处理;在 NSString+Utils.h 和 NSString+Utils.m 中新增图片格式验证方法,提升代码健壮性和可读性。

This commit is contained in:
edwinQQQ
2025-06-27 19:56:03 +08:00
parent 0d6570a086
commit f0dad8886f
14 changed files with 748 additions and 56 deletions

View File

@@ -22,12 +22,17 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, copy) NSString *picUrl;
@property (nonatomic, assign) BOOL useStatus;
/// 将 expireSeconds 转换为 "yyyy/MM/dd" 格式的字符串
- (NSString *)expireDateString;
@end
@interface MedalSeriesItemVo : PIBaseModel
@property (nonatomic, assign) NSInteger medalLevel;
@property (nonatomic, copy) NSString *seriesName;
@property (nonatomic, copy) NSString *mp4Url;
@property (nonatomic, copy) NSString *picUrl;
@property (nonatomic, copy) NSArray <MedalVo *> *medalVos;
@end

View File

@@ -9,6 +9,23 @@
@implementation MedalVo
/// expireSeconds "yyyy/MM/dd"
- (NSString *)expireDateString {
if (self.expireSeconds <= 0) {
return YMLocalizedString(@"20.20.61_text_9");
}
// NSDate
NSDate *expireDate = [NSDate dateWithTimeIntervalSince1970:self.expireSeconds];
//
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy/MM/dd";
//
return [NSString stringWithFormat:YMLocalizedString(@"20.20.61_text_8"), [formatter stringFromDate:expireDate]];
}
@end
@implementation MedalSeriesItemVo

View File

@@ -12,7 +12,7 @@
#import "MedalsLevelIndicatorView.h"
@interface MedalsCollectionViewCell ()
@interface MedalsCollectionViewCell () <VAPWrapViewDelegate, HWDMP4PlayDelegate>
@property(nonatomic, copy) NSString *imagePath;
@property(nonatomic, copy) NSString *mp4Path;
@@ -154,10 +154,19 @@
self.mp4Path = nil;
self.imagePath = nil;
self.isVisible = NO;
self.displayModel = nil; //
self.currentItemVo = nil; //
//
self.titleLabel.text = @"";
self.subLabel.text = @"";
//
[self.levelIndicatorView resetToLevel:0];
//
self.imageView.image = nil;
self.imageView.imageUrl = @"";
}
- (void)updateCell:(MedalSeriesVo *)model {
@@ -172,11 +181,12 @@
//
self.levelIndicatorView.indicatorType = MedalsLevelIndicatorTypeNormal;
//
// 使 URL
for (NSInteger i = 0; i < itemVos.medalVos.count; i++) {
MedalVo *medalVo = [itemVos.medalVos xpSafeObjectAtIndex:i];
if (medalVo) {
[self.levelIndicatorView setImageUrl:medalVo.picUrl forLevel:i + 1];
NSString *imageUrl = [self getImageUrlForMedal:medalVo];
[self.levelIndicatorView setImageUrl:imageUrl forLevel:i + 1];
}
}
@@ -184,21 +194,104 @@
}
- (void)updateDisplayWithCurrentModel {
if (self.displayModel) {
if ([self.displayModel.picUrl hasSuffix:@"mp4"]) {
[self setMp4Path:self.displayModel.picUrl];
} else {
[self setImagePath:self.displayModel.picUrl];
if (!self.displayModel || !self.currentItemVo) {
[self showDefaultPlaceholder];
return;
}
// TODO: fix mp4
// MP4 URL
NSString *mp4Url = self.currentItemVo.mp4Url;
NSString *picUrl = self.currentItemVo.picUrl;
// MP4 URL
if (![NSString isEmpty:mp4Url] && [self isValidMP4URL:mp4Url]) {
[self setMp4Path:mp4Url];
[self updateTextLabels];
return;
}
// picUrl MP4
if (![NSString isEmpty:picUrl]) {
if ([self isValidMP4URL:picUrl]) {
[self setMp4Path:picUrl];
[self updateTextLabels];
return;
} else if ([NSString isValidImageURL:picUrl]) {
[self setImagePath:picUrl];
[self updateTextLabels];
return;
}
self.titleLabel.text = self.displayModel.name;
if (self.displayModel.expireSeconds == 0) {
self.subLabel.text = YMLocalizedString(@"20.20.61_text_9");
}
//
[self showDefaultPlaceholder];
//
self.titleLabel.text = self.displayModel.name;
self.subLabel.text = [self.displayModel expireDateString];
}
#pragma mark -
/// MP4 URL
- (BOOL)isValidMP4URL:(NSString *)url {
if ([NSString isEmpty:url]) {
return NO;
}
// URL mp4 mp4
NSString *lowercaseUrl = [url lowercaseString];
return [lowercaseUrl hasSuffix:@".mp4"] ||
[lowercaseUrl containsString:@"mp4"] ||
[lowercaseUrl containsString:@"video"];
}
///
- (void)updateTextLabels {
self.titleLabel.text = self.displayModel.name;
self.subLabel.text = [self.displayModel expireDateString];
}
///
- (void)showDefaultPlaceholder {
[self stopMP4Playback];
self.mp4View.hidden = YES;
self.imageView.hidden = NO;
//
self.imageView.imageUrl = @"";
self.imageView.image = [UIImageConstant defaultEmptyPlaceholder];
}
/// URL
- (NSString *)getImageUrlForMedal:(MedalVo *)medal {
if (!medal) {
return @"";
}
#if 0
// Debug 使
if ([NSString isEmpty:medal.mp4Url] && ![NSString isEmpty:medal.picUrl]) {
return medal.picUrl;
}
#endif
// 使 mp4URL
if (![NSString isEmpty:medal.mp4Url]) {
return medal.mp4Url;
} else if (![NSString isEmpty:medal.picUrl]) {
// mp4URL 使 picURL ()
if ([NSString isValidImageURL:medal.picUrl]) {
return medal.picUrl;
} else {
self.subLabel.text = [NSString stringWithFormat:YMLocalizedString(@"20.20.61_text_8"),
[NSDate timestampSwitchTime:self.displayModel.expireSeconds
formatter:@"yyyy/mm/dd"]];
// picURL
return @"";
}
} else {
//
return @"";
}
}
@@ -234,19 +327,43 @@
completionBlock:^(NSString * _Nullable videoUrl) {
@kStrongify(self);
if (![NSString isEmpty:videoUrl]) {
// cell
//
self.mp4View.tag = 1; //
// Cell
if (self.isVisible) {
[self.mp4View playHWDMP4:videoUrl repeatCount:-1 delegate:nil];
} else {
// URL
self.mp4View.tag = 1; //
[self startMP4PlaybackWithURL:videoUrl];
}
}
} failureBlock:^(NSError * _Nullable error) {
NSLog(@"Failed to parse mp4: %@", error);
@kStrongify(self);
NSLog(@"[MedalsCollectionViewCell] Failed to parse mp4: %@, fallback to image", error);
// MP4 使 picURL
[self handleMP4FailureWithFallback];
}];
}
/// MP4
- (void)startMP4PlaybackWithURL:(NSString *)videoUrl {
if (![NSString isEmpty:videoUrl] && self.mp4View && !self.mp4View.hidden) {
[self.mp4View playHWDMP4:videoUrl repeatCount:-1 delegate:self];
NSLog(@"[MedalsCollectionViewCell] Started MP4 playback: %@", videoUrl);
}
}
/// MP4 使 picURL
- (void)handleMP4FailureWithFallback {
if (![NSString isEmpty:self.displayModel.picUrl]) {
if ([NSString isValidImageURL:self.displayModel.picUrl]) {
[self setImagePath:self.displayModel.picUrl];
} else {
[self showDefaultPlaceholder];
}
} else {
[self showDefaultPlaceholder];
}
}
#pragma mark - MP4
- (void)stopMP4Playback {
@@ -270,20 +387,58 @@
completionBlock:^(NSString * _Nullable videoUrl) {
@kStrongify(self);
if (![NSString isEmpty:videoUrl] && self.isVisible) {
[self.mp4View playHWDMP4:videoUrl repeatCount:-1 delegate:nil];
[self startMP4PlaybackWithURL:videoUrl];
}
} failureBlock:nil];
} failureBlock:^(NSError * _Nullable error) {
@kStrongify(self);
// MP4 使 picURL
[self handleMP4FailureWithFallback];
}];
} else {
[self.mp4View resumeHWDMP4];
}
}
}
- (void)vapWrap_viewDidFailPlayMP4:(NSError *)error {
NSLog(@"%@", error);
}
#pragma mark - HWDMP4PlayDelegate
- (BOOL)shouldStartPlayMP4:(VAPView *)container config:(QGVAPConfigModel *)config {
return YES;
}
- (void)viewDidFinishPlayMP4:(NSInteger)totalFrameCount view:(VAPView *)container {
// MP4
}
- (void)viewDidStopPlayMP4:(NSInteger)lastFrameIndex view:(VAPView *)container {
// MP4
}
- (void)viewDidFailPlayMP4:(NSError *)error {
NSLog(@"MP4 播放失败: %@", error);
// MP4 使 picURL
[self handleMP4FailureWithFallback];
}
#pragma mark -
- (void)willDisplay {
self.isVisible = YES;
[self resumeMP4Playback];
// MP4
if (self.mp4View.tag == 1 && !self.mp4View.hidden && self.mp4Path) {
[self resumeMP4Playback];
} else if (!self.mp4View.hidden) {
//
[self resumeMP4Playback];
}
NSLog(@"[MedalsCollectionViewCell] willDisplay - isVisible: %@, mp4Path: %@",
self.isVisible ? @"YES" : @"NO", self.mp4Path ?: @"nil");
}
- (void)didEndDisplaying {
@@ -327,7 +482,7 @@
- (VAPView *)mp4View {
if (!_mp4View) {
_mp4View = [[VAPView alloc] init];
_mp4View.contentMode = UIViewContentModeScaleAspectFit;
_mp4View.contentMode = UIViewContentModeScaleAspectFill;
}
return _mp4View;
}

View File

@@ -10,7 +10,7 @@
#import <QGVAPWrapView.h>
#import "XPRoomGiftAnimationParser.h"
@interface MedalsCyclePagerCell ()
@interface MedalsCyclePagerCell () <HWDMP4PlayDelegate>
@property(nonatomic, copy) NSString *imagePath;
@property(nonatomic, copy) NSString *mp4Path;
@@ -99,9 +99,11 @@
self.mp4Path = nil;
self.imagePath = nil;
self.isVisible = YES;
self.displayModel = nil; //
//
self.imageView.image = nil;
self.imageView.imageUrl = @"";
}
- (void)updateCell:(MedalVo *)model {
@@ -110,13 +112,60 @@
}
- (void)updateDisplayWithCurrentModel {
if (self.displayModel) {
if ([self.displayModel.picUrl hasSuffix:@"mp4"]) {
[self setMp4Path:self.displayModel.picUrl];
} else {
[self setImagePath:self.displayModel.picUrl];
if (!self.displayModel) {
[self showDefaultPlaceholder];
return;
}
// MP4 URL
NSString *mp4Url = self.displayModel.mp4Url;
NSString *picUrl = self.displayModel.picUrl;
// MP4 URL
if (![NSString isEmpty:mp4Url] && [self isValidMP4URL:mp4Url]) {
[self setMp4Path:mp4Url];
return;
}
// picUrl MP4
if (![NSString isEmpty:picUrl]) {
if ([self isValidMP4URL:picUrl]) {
[self setMp4Path:picUrl];
return;
} else if ([NSString isValidImageURL:picUrl]) {
[self setImagePath:picUrl];
return;
}
}
//
[self showDefaultPlaceholder];
}
#pragma mark -
/// MP4 URL
- (BOOL)isValidMP4URL:(NSString *)url {
if ([NSString isEmpty:url]) {
return NO;
}
// URL mp4 mp4
NSString *lowercaseUrl = [url lowercaseString];
return [lowercaseUrl hasSuffix:@".mp4"] ||
[lowercaseUrl containsString:@"mp4"] ||
[lowercaseUrl containsString:@"video"];
}
///
- (void)showDefaultPlaceholder {
[self stopMP4Playback];
self.mp4View.hidden = YES;
self.imageView.hidden = NO;
// 使
self.imageView.imageUrl = @"";
self.imageView.image = [UIImageConstant defaultEmptyPlaceholder];
}
- (void)setImagePath:(NSString *)imagePath {
@@ -151,19 +200,43 @@
completionBlock:^(NSString * _Nullable videoUrl) {
@kStrongify(self);
if (![NSString isEmpty:videoUrl]) {
// cell
//
self.mp4View.tag = 1; //
// Cell
if (self.isVisible) {
[self.mp4View playHWDMP4:videoUrl repeatCount:-1 delegate:nil];
} else {
// URL
self.mp4View.tag = 1; //
[self startMP4PlaybackWithURL:videoUrl];
}
}
} failureBlock:^(NSError * _Nullable error) {
NSLog(@"Failed to parse mp4: %@", error);
@kStrongify(self);
NSLog(@"[MedalsCyclePagerCell] Failed to parse mp4: %@, fallback to image", error);
// MP4 使 picURL
[self handleMP4FailureWithFallback];
}];
}
/// MP4
- (void)startMP4PlaybackWithURL:(NSString *)videoUrl {
if (![NSString isEmpty:videoUrl] && self.mp4View && !self.mp4View.hidden) {
[self.mp4View playHWDMP4:videoUrl repeatCount:-1 delegate:self];
NSLog(@"[MedalsCyclePagerCell] Started MP4 playback: %@", videoUrl);
}
}
/// MP4 使 picURL
- (void)handleMP4FailureWithFallback {
if (![NSString isEmpty:self.displayModel.picUrl]) {
if ([NSString isValidImageURL:self.displayModel.picUrl]) {
[self setImagePath:self.displayModel.picUrl];
} else {
[self showDefaultPlaceholder];
}
} else {
[self showDefaultPlaceholder];
}
}
#pragma mark - MP4
- (void)stopMP4Playback {
@@ -187,9 +260,13 @@
completionBlock:^(NSString * _Nullable videoUrl) {
@kStrongify(self);
if (![NSString isEmpty:videoUrl] && self.isVisible) {
[self.mp4View playHWDMP4:videoUrl repeatCount:-1 delegate:nil];
[self startMP4PlaybackWithURL:videoUrl];
}
} failureBlock:nil];
} failureBlock:^(NSError * _Nullable error) {
@kStrongify(self);
// MP4 使 picURL
[self handleMP4FailureWithFallback];
}];
} else {
[self.mp4View resumeHWDMP4];
}
@@ -200,7 +277,17 @@
- (void)willDisplay {
self.isVisible = YES;
[self resumeMP4Playback];
// MP4
if (self.mp4View.tag == 1 && !self.mp4View.hidden && self.mp4Path) {
[self resumeMP4Playback];
} else if (!self.mp4View.hidden) {
//
[self resumeMP4Playback];
}
NSLog(@"[MedalsCyclePagerCell] willDisplay - isVisible: %@, mp4Path: %@",
self.isVisible ? @"YES" : @"NO", self.mp4Path ?: @"nil");
}
- (void)didEndDisplaying {
@@ -247,8 +334,31 @@
if (!_mp4View) {
_mp4View = [[VAPView alloc] init];
_mp4View.contentMode = UIViewContentModeScaleAspectFit;
#if DEBUG
_mp4View.backgroundColor = [UIColor redColor];
#endif
}
return _mp4View;
}
#pragma mark - HWDMP4PlayDelegate
- (BOOL)shouldStartPlayMP4:(VAPView *)container config:(QGVAPConfigModel *)config {
return YES;
}
- (void)viewDidFinishPlayMP4:(NSInteger)totalFrameCount view:(VAPView *)container {
// MP4
}
- (void)viewDidStopPlayMP4:(NSInteger)lastFrameIndex view:(VAPView *)container {
// MP4
}
- (void)viewDidFailPlayMP4:(NSError *)error {
NSLog(@"MP4 播放失败: %@", error);
// MP4 使 picURL
[self handleMP4FailureWithFallback];
}
@end

View File

@@ -130,11 +130,12 @@
//
self.levelIndicatorView.indicatorType = MedalsLevelIndicatorTypeWithImage;
//
// 使 URL
for (NSInteger i = 0; i < self.currentSeriesItemVO.medalVos.count; i++) {
MedalVo *medalVo = [self.currentSeriesItemVO.medalVos xpSafeObjectAtIndex:i];
if (medalVo) {
[self.levelIndicatorView setImageUrl:medalVo.picUrl forLevel:i + 1];
NSString *imageUrl = [self getImageUrlForMedal:medalVo];
[self.levelIndicatorView setImageUrl:imageUrl forLevel:i + 1];
}
}
@@ -142,15 +143,86 @@
}
- (void)updateDisplayWithCurrentModel {
if (self.displayModel) {
if (!self.displayModel) {
[self showDefaultPlaceholder];
return;
}
#if 0
// Debug 使
if ([NSString isEmpty:self.displayModel.mp4Url] && ![NSString isEmpty:self.displayModel.picUrl]) {
// 使 picUrl
if ([self.displayModel.picUrl hasSuffix:@"mp4"]) {
[self setMp4Path:self.displayModel.picUrl];
} else {
[self setImagePath:self.displayModel.picUrl];
}
self.titleLabel.text = self.displayModel.name;
self.subLabel.text = self.displayModel.medalDesc;
return;
}
#endif
// 使 mp4URL
if (![NSString isEmpty:self.displayModel.mp4Url]) {
[self setMp4Path:self.displayModel.mp4Url];
} else if (![NSString isEmpty:self.displayModel.picUrl]) {
// mp4URL 使 picURL
if ([NSString isValidImageURL:self.displayModel.picUrl]) {
[self setImagePath:self.displayModel.picUrl];
} else {
// picURL
[self showDefaultPlaceholder];
}
} else {
//
[self showDefaultPlaceholder];
}
self.titleLabel.text = self.displayModel.name;
self.subLabel.text = self.displayModel.medalDesc;
}
#pragma mark -
///
- (void)showDefaultPlaceholder {
[self stopMP4Playback];
self.mp4View.hidden = YES;
self.imageView.hidden = NO;
//
self.imageView.imageUrl = @"";
self.imageView.image = [UIImageConstant defaultEmptyPlaceholder];
}
/// URL
- (NSString *)getImageUrlForMedal:(MedalVo *)medal {
if (!medal) {
return @"";
}
#if 0
// Debug 使
if ([NSString isEmpty:medal.mp4Url] && ![NSString isEmpty:medal.picUrl]) {
return medal.picUrl;
}
#endif
// 使 mp4URL
if (![NSString isEmpty:medal.mp4Url]) {
return medal.mp4Url;
} else if (![NSString isEmpty:medal.picUrl]) {
// mp4URL 使 picURL ()
if ([NSString isValidImageURL:medal.picUrl]) {
return medal.picUrl;
} else {
// picURL
return @"";
}
} else {
//
return @"";
}
}
@@ -166,9 +238,9 @@
- (void)setMp4Path:(NSString *)mp4Path {
// mp4
if ([_mp4Path isEqualToString:mp4Path]) {
return;
}
// if ([_mp4Path isEqualToString:mp4Path]) {
// return;
// }
// mp4
[self stopMP4Playback];
@@ -189,10 +261,26 @@
[self.mp4View playHWDMP4:videoUrl repeatCount:-1 delegate:nil];
}
} failureBlock:^(NSError * _Nullable error) {
@kStrongify(self);
NSLog(@"Failed to parse mp4: %@", error);
// MP4 使 picURL
[self handleMP4FailureWithFallback];
}];
}
/// MP4 使 picURL
- (void)handleMP4FailureWithFallback {
if (![NSString isEmpty:self.displayModel.picUrl]) {
if ([NSString isValidImageURL:self.displayModel.picUrl]) {
[self setImagePath:self.displayModel.picUrl];
} else {
[self showDefaultPlaceholder];
}
} else {
[self showDefaultPlaceholder];
}
}
#pragma mark - MP4
- (void)stopMP4Playback {
@@ -218,7 +306,11 @@
if (![NSString isEmpty:videoUrl]) {
[self.mp4View playHWDMP4:videoUrl repeatCount:-1 delegate:nil];
}
} failureBlock:nil];
} failureBlock:^(NSError * _Nullable error) {
@kStrongify(self);
// MP4 使 picURL
[self handleMP4FailureWithFallback];
}];
} else {
[self.mp4View resumeHWDMP4];
}

View File

@@ -23,6 +23,12 @@ typedef NS_ENUM(NSInteger, MedalsLevelIndicatorType) {
- (void)setupWithMaxLevel:(NSInteger)maxLevel;
- (void)setSelectedLevel:(NSInteger)level animated:(BOOL)animated;
/**
* 重置等级指示器到指定等级
* @param level 等级0表示重置到初始状态
*/
- (void)resetToLevel:(NSInteger)level;
/**
* 设置指定等级的图片
* @param imageUrl 图片URL

View File

@@ -141,7 +141,17 @@
}
- (void)setImageUrl:(NSString *)imageUrl {
// MP4
// URL
#if 0
imageUrl = @"https://img.toto.im/mw600/9f0b0dd5ly1i2u07wq7g2j20n00w343s.jpg.webp";
#endif
if ([NSString isEmpty:imageUrl]) {
[self showDefaultPlaceholder];
return;
}
#if 0
// Debug 使
if ([imageUrl.lowercaseString hasSuffix:@"mp4"]) {
[self setMP4Url:imageUrl];
} else {
@@ -157,6 +167,50 @@
[self loadImageWithUrl:imageUrl];
}
}
return;
#endif
// MP4
if ([imageUrl.lowercaseString hasSuffix:@"mp4"]) {
// MP4 URLMP4
[self showDefaultPlaceholder];
// [self setMP4Url:imageUrl]; //
} else if ([NSString isValidImageURL:imageUrl]) {
// URL
_hasImage = YES;
_hasMP4 = NO;
_cachedImageUrl = imageUrl;
// MP4
[self stopMP4Playback];
// imageView
if (_imageView) {
[self loadImageWithUrl:imageUrl];
}
} else {
// URL
[self showDefaultPlaceholder];
}
}
///
- (void)showDefaultPlaceholder {
// MP4
[self stopMP4Playback];
_hasImage = NO;
_hasMP4 = NO;
_cachedImageUrl = nil;
_cachedMP4Url = nil;
if (_imageView) {
_imageView.hidden = NO;
_imageView.image = [UIImageConstant defaultEmptyPlaceholder];
}
if (_mp4View) {
_mp4View.hidden = YES;
}
}
- (void)setMP4Url:(NSString *)mp4Url {
@@ -182,6 +236,7 @@
@kStrongify(self);
if (image) {
UIImage *cutImage = [image cutImage:CGSizeMake(40, 40)];
self.hasImage = YES;
self.originalImage = cutImage;
self.grayImage = [cutImage grayscaleImage];
[self updateImageEffect];
@@ -214,8 +269,10 @@
});
}
} failureBlock:^(NSError * _Nullable error) {
@kStrongify(self);
// MP4
NSLog(@"MP4 加载失败: %@", error);
[self showDefaultPlaceholder];
}];
}
@@ -239,13 +296,13 @@
}
// MP4 - 使 alpha
if (_hasMP4 && _mp4View) {
if (_isSelected) {
_mp4View.alpha = 1.0; //
} else {
_mp4View.alpha = 0.3; //
}
}
// if (_hasMP4 && _mp4View) {
// if (_isSelected) {
// _mp4View.alpha = 1.0; //
// } else {
// _mp4View.alpha = 0.3; //
// }
// }
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
@@ -484,6 +541,22 @@
}
}
- (void)resetToLevel:(NSInteger)level {
// MP4
for (LevelItemView *item in _levelItems) {
[item stopMP4Playback];
}
//
if (level <= 0) {
//
[self setSelectedLevel:1 animated:NO];
} else {
//
[self setSelectedLevel:level animated:NO];
}
}
- (void)setIndicatorType:(MedalsLevelIndicatorType)indicatorType {
_indicatorType = indicatorType;

View File

@@ -815,6 +815,15 @@ typedef enum : NSInteger {
[self.medalsCollectionView reloadData];
}
#pragma mark -
- (void)refreshDataAfterWearingChange {
// Mine
if (self.displayType == MedalsCenterDisplayType_Mine) {
// tab
[self loadMedalsList:self.currentTabType page:1];
}
}
#pragma mark - Button actions
- (void)didTapSquareButton:(UIButton *)sender {
MedalsViewController *vc = [[MedalsViewController alloc] initForMedalsSquare];
@@ -828,12 +837,30 @@ typedef enum : NSInteger {
- (void)didTapWearingButton:(UIButton *)sender {
MedalsWearingViewController *vc = [[MedalsWearingViewController alloc] init];
//
@kWeakify(self);
vc.dataChangedCallback = ^{
@kStrongify(self);
//
[self refreshDataAfterWearingChange];
};
[self addChildViewController:vc];
[self.view addSubview:vc.view];
}
- (void)didTapEmptyMedalButton:(UIButton *)sender {
MedalsWearingViewController *vc = [[MedalsWearingViewController alloc] init];
//
@kWeakify(self);
vc.dataChangedCallback = ^{
@kStrongify(self);
//
[self refreshDataAfterWearingChange];
};
[self addChildViewController:vc];
[self.view addSubview:vc.view];
}
@@ -952,6 +979,9 @@ typedef enum : NSInteger {
if (self.useMedals.count > 1 && self.displayType == MedalsCenterDisplayType_Square) {
[self startAutoScroll:toIndex];
}
MedalVo *vo = [self.useMedals xpSafeObjectAtIndex:toIndex];
self.medalDescLabel.text = [vo expireDateString];
}
#pragma mark - Lazy load

View File

@@ -88,6 +88,66 @@
self.medalImageView.imageUrl = @"";
return;
}
// Cell
[self handleMedalDisplay:medalVo];
}
#pragma mark -
- (void)handleMedalDisplay:(MedalVo *)medalVo {
if (!medalVo) {
self.medalImageView.imageUrl = @"";
return;
}
#ifdef DEBUG
// Debug 使
if ([NSString isEmpty:medalVo.mp4Url] && [NSString isEmpty:medalVo.picUrl]) {
// 使
[self handleLegacyDisplay:medalVo];
return;
}
if (![NSString isEmpty:medalVo.picUrl] && [NSString isEmpty:medalVo.mp4Url]) {
// picUrl mp4Url
if (![NSString isImageFormat:medalVo.picUrl] && ![medalVo.picUrl.lowercaseString hasSuffix:@".mp4"]) {
// picUrl mp4使
[self handleLegacyDisplay:medalVo];
return;
}
}
#endif
// 1. mp4Url
if (![NSString isEmpty:medalVo.mp4Url]) {
// Cell mp4使 picUrl
if (![NSString isEmpty:medalVo.picUrl] && [NSString isImageFormat:medalVo.picUrl]) {
self.medalImageView.imageUrl = medalVo.picUrl;
} else {
// picUrl
self.medalImageView.imageUrl = @"";
}
return;
}
// 2. mp4Url 使 picUrl
if (![NSString isEmpty:medalVo.picUrl]) {
// 3. 使 picUrl
if ([NSString isImageFormat:medalVo.picUrl]) {
self.medalImageView.imageUrl = medalVo.picUrl;
} else {
//
self.medalImageView.image = [UIImageConstant defaultEmptyPlaceholder];
}
return;
}
// 4.
self.medalImageView.imageUrl = @"";
}
#pragma mark - Debug
- (void)handleLegacyDisplay:(MedalVo *)medalVo {
if (![medalVo.picUrl hasSuffix:@"mp4"]) {
self.medalImageView.imageUrl = medalVo.picUrl;
}
@@ -122,6 +182,7 @@
NetImageConfig *config = [[NetImageConfig alloc] init];
config.placeHolder = kImage(@"medals_control_position");
_medalImageView = [[NetImageView alloc] initWithConfig:config];
_medalImageView.contentMode = UIViewContentModeScaleAspectFill;
}
return _medalImageView;
}

View File

@@ -22,6 +22,9 @@
@property (nonatomic, assign) BOOL isVisible; // cell
//
@property (nonatomic, strong) MedalVo *currentMedalModel; // medal
@end
@implementation MedalsWearingListCollectionViewCell
@@ -43,6 +46,84 @@
- (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 {
@@ -135,6 +216,8 @@
}
} failureBlock:^(NSError * _Nullable error) {
NSLog(@"Failed to parse mp4: %@", error);
// mp4 使 picUrl
[self handleMp4FailureFallback];
}];
}
@@ -264,6 +347,7 @@
self.mp4Path = nil;
self.imagePath = nil;
self.isVisible = NO;
self.currentMedalModel = nil; //
}
@end

View File

@@ -11,6 +11,9 @@ NS_ASSUME_NONNULL_BEGIN
@interface MedalsWearingViewController : MvpViewController
/// 数据变化回调,当佩戴数据发生改变时触发
@property (nonatomic, copy) void (^dataChangedCallback)(void);
@end
NS_ASSUME_NONNULL_END

View File

@@ -26,6 +26,9 @@
@property (nonatomic, copy) NSDictionary *vipSeatDic;
@property (nonatomic, assign) NSInteger minVipLevelForSeats;
//
@property (nonatomic, assign) BOOL hasDataChanged;
@end
@implementation MedalsWearingViewController
@@ -42,6 +45,7 @@
[super viewDidLoad];
self.medalsAreaPage = 1;
self.hasDataChanged = NO; //
[self setupUI];
[self setupRefresh];
@@ -88,6 +92,15 @@
#pragma mark - User Actions
- (void)handleTapGesture:(id)sender {
[self dismissViewController];
}
- (void)dismissViewController {
//
if (self.hasDataChanged && self.dataChangedCallback) {
self.dataChangedCallback();
}
[self.view removeFromSuperview];
[self removeFromParentViewController];
}
@@ -123,6 +136,8 @@
}
- (void)useMedalSuccess {
//
self.hasDataChanged = YES;
[self headerRefresh];
}

View File

@@ -53,6 +53,11 @@ NS_ASSUME_NONNULL_BEGIN
+ (NSString *)trimString:(NSString *)input lengthLimit:(NSInteger)limit;
+ (BOOL)isValidImageURL:(NSString *)url;
/// 检查字符串是否为图片格式(通过扩展名判断)
+ (BOOL)isImageFormat:(NSString *)url;
@end

View File

@@ -516,4 +516,40 @@
}
}
/// URL
+ (BOOL)isValidImageURL:(NSString *)url {
if ([NSString isEmpty:url]) {
return NO;
}
NSString *lowercaseURL = url.lowercaseString;
NSArray *imageExtensions = @[@"png", @"jpg", @"jpeg", @"gif", @"webp", @"bmp", @"svg"];
for (NSString *extension in imageExtensions) {
if ([lowercaseURL hasSuffix:extension]) {
return YES;
}
}
return NO;
}
///
+ (BOOL)isImageFormat:(NSString *)url {
if ([NSString isEmpty:url]) {
return NO;
}
NSString *lowercaseURL = url.lowercaseString;
NSArray *imageExtensions = @[@".png", @".jpg", @".jpeg", @".gif", @".webp", @".bmp", @".svg"];
for (NSString *extension in imageExtensions) {
if ([lowercaseURL hasSuffix:extension]) {
return YES;
}
}
return NO;
}
@end