// // MedalsViewController.m // YuMi // // Created by P on 2025/6/17. // #import "MedalsViewController.h" #import "MedalsPresenter.h" #import "UserInfoModel.h" #import "TYCyclePagerView.h" #import "MedalsCollectionViewCell.h" #import "MedalsCyclePagerCell.h" #import "MedalsDetailView.h" #import "MedalsWearingViewController.h" #import "MedalsRankViewController.h" #import #import "XPRoomGiftAnimationParser.h" typedef enum : NSInteger { MedalsCenterTab_TaskMedals = 1, MedalsCenterTab_ActivityMedals = 2, MedalsCenterTab_GloryMedals = 3, } MedalsCenterTabType; typedef enum : NSInteger { MedalsCenterDisplayType_Mine, MedalsCenterDisplayType_Other, MedalsCenterDisplayType_Square } MedalsCenterDisplayType; @interface MedalsViewController () @property (nonatomic, strong) UserInfoModel *userInfo; @property (nonatomic, copy) NSArray *centerTabButtons; @property (nonatomic, strong) UILabel *medalDescLabel; @property (nonatomic, strong) UIButton *emptyUserMedalButton; @property (nonatomic, strong) TYCyclePagerView *medalsCyclePagerView; @property (nonatomic, strong) UIView *emptyView; @property (nonatomic, strong) UICollectionView *medalsCollectionView; @property (nonatomic, strong) UIStackView *centerTabsStackView; @property (nonatomic, strong) NSMutableArray *datasourceTaskMedals; @property (nonatomic, strong) NSMutableArray *datasourceActivityMedals; @property (nonatomic, strong) NSMutableArray *datasourceGloryMedals; @property (nonatomic, copy) NSArray *useMedals; @property (nonatomic, assign) NSInteger currentPageTaskMedals; @property (nonatomic, assign) NSInteger currentPageActivityMedals; @property (nonatomic, assign) NSInteger currentPageGloryMedals; @property (nonatomic, assign) MedalsCenterTabType currentTabType; @property (nonatomic, assign) MedalsCenterDisplayType displayType; @property (nonatomic, strong) UserMedalsModel *userMedalsModel; @property (nonatomic, strong) NSMutableArray *taskSquareMedalVo; @property (nonatomic, strong) NSMutableArray *activitySquareMedalVo; @property (nonatomic, strong) NSMutableArray *glorySquareMedalVo; @property (nonatomic, strong) UIImageView *otherBG; @property (nonatomic, strong) NetImageView *otherAvatar; @property (nonatomic, strong) NetImageView *otherMedal; @property (nonatomic, strong) VAPView *otherMP4View; @property (nonatomic, strong) XPRoomGiftAnimationParser *mp4Parser; @property (nonatomic, strong) UILabel *otherNameLabel; @property (nonatomic, strong) UILabel *otherCountLabel; @property (nonatomic, strong) UserMedalsModel *otherMedalsModel; // 自动轮播相关属性 @property (nonatomic, strong) NSTimer *autoScrollTimer; @property (nonatomic, assign) NSInteger currentAutoScrollIndex; @end @implementation MedalsViewController - (instancetype)initForMyMedals:(UserInfoModel *)userInfo { self = [super init]; if (self) { self.displayType = MedalsCenterDisplayType_Mine; self.userInfo = userInfo; } return self; } - (instancetype)initForOtherMedals:(UserInfoModel *)userInfo { self = [super init]; if (self) { self.displayType = MedalsCenterDisplayType_Other; self.userInfo = userInfo; } return self; } - (instancetype)initForMedalsSquare { self = [super init]; if (self) { self.displayType = MedalsCenterDisplayType_Square; } return self; } - (BOOL)isHiddenNavBar { return YES; } - (MedalsPresenter *)createPresenter { return [[MedalsPresenter alloc] init]; } - (void)dealloc { [self stopAutoScroll]; [self stopOtherMedalMP4Playback]; } - (void)viewDidLoad { [super viewDidLoad]; [self setupUI]; [self setupData]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; // 确保可见的 cell 开始播放 [self startVisibleCellsPlayback]; // 如果有佩戴的勋章,再次确保居中显示 if (self.useMedals.count > 0 && !self.medalsCyclePagerView.hidden) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self.medalsCyclePagerView scrollToItemAtIndex:0 animate:NO]; }); } // 重新启动自动轮播(如果有多个勋章且在广场页面) if (self.useMedals.count > 1 && !self.medalsCyclePagerView.hidden && self.displayType == MedalsCenterDisplayType_Square) { [self startAutoScroll:self.currentAutoScrollIndex]; } } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; // 停止所有播放 [self stopAllCellsPlayback]; // 停止自动轮播 [self stopAutoScroll]; // 停止其他用户勋章的MP4播放 [self stopOtherMedalMP4Playback]; } - (void)stopAllCellsPlayback { // 停止所有 cell 的播放 NSArray *visibleCells = self.medalsCyclePagerView.collectionView.visibleCells; for (UICollectionViewCell *cell in visibleCells) { if ([cell isKindOfClass:[MedalsCyclePagerCell class]]) { [(MedalsCyclePagerCell *)cell didEndDisplaying]; } } } #pragma mark - 自动轮播方法 /** * 启动自动轮播 * @param userSelectedIndex 用户选择的起始索引,如果无效则默认从0开始 */ - (void)startAutoScroll:(NSInteger)userSelectedIndex { [self stopAutoScroll]; // 先停止之前的定时器 // 设置当前索引 if (userSelectedIndex >= 0 && userSelectedIndex < self.useMedals.count) { self.currentAutoScrollIndex = userSelectedIndex; } else { self.currentAutoScrollIndex = 0; } NSTimeInterval time = 5; // 总是创建新的定时器(因为已经在上面停止了旧的) self.autoScrollTimer = [NSTimer scheduledTimerWithTimeInterval:time target:self selector:@selector(autoScrollToNextItem) userInfo:nil repeats:YES]; } - (void)stopAutoScroll { if (self.autoScrollTimer) { [self.autoScrollTimer invalidate]; self.autoScrollTimer = nil; } } - (void)autoScrollToNextItem { if (self.useMedals.count <= 1) { [self stopAutoScroll]; return; } // 计算下一个索引,实现循环滚动 self.currentAutoScrollIndex = (self.currentAutoScrollIndex + 1) % self.useMedals.count; // 滚动到下一个位置 [self.medalsCyclePagerView scrollToItemAtIndex:self.currentAutoScrollIndex animate:YES]; } /** * Tab 切换时重置自动滚动状态 * 停止当前定时器,重置索引为0,准备从第一个元素重新开始轮播 */ - (void)resetAutoScrollOnTabChange { // 停止当前的自动滚动定时器 [self stopAutoScroll]; // 重置滚动索引为 0,确保切换 tab 后从第一个元素开始显示 self.currentAutoScrollIndex = 0; // 注意:新的自动滚动将在数据更新后的 _updateWearingInfo 方法中重新启动 } #pragma mark - UI - (void)setupUI { [self setupBackground]; if (self.displayType != MedalsCenterDisplayType_Other) { if (self.userInfo.medals.medalCount == 0) { [self setupEmptyUserMedals]; } else { [self setupWearingUserMedals]; } } else { [self setupOthersMedalInfo]; } [self setupCenterTabs]; [self setupBottomMedalsList]; [self setupEmptyView]; [self setupNavigationBar]; if (self.displayType == MedalsCenterDisplayType_Mine) { [self setupWearingButton]; } } - (void)setupNavigationBar { NSString *title = @""; NSArray *navigationButtons = [NSArray array]; switch (self.displayType) { case MedalsCenterDisplayType_Mine: title = YMLocalizedString(@"20.20.61_text_1"); navigationButtons = @[[self medalsRankButton], [self medalsSquareButton]]; break; case MedalsCenterDisplayType_Other: title = [NSString stringWithFormat:YMLocalizedString(@"20.20.61_text_14"), self.userInfo.nick]; break; case MedalsCenterDisplayType_Square: title = YMLocalizedString(@"20.20.61_text_10"); break; default: break; } [self setupCustomNavigationBar:^{} title:title titleColor:[UIColor whiteColor]]; [self setupCustonNavigationRightButtons:navigationButtons sizes:@[ [NSValue valueWithCGSize:CGSizeMake(22, 22)], [NSValue valueWithCGSize:CGSizeMake(22, 22)] ]]; } - (void)setupBackground { self.view.backgroundColor = UIColorFromRGB(0x1B0043); UIImageView *bottomBg = [self bottomBG]; [self.view addSubview:bottomBg]; if (self.displayType == MedalsCenterDisplayType_Other) { [bottomBg mas_makeConstraints:^(MASConstraintMaker *make) { make.bottom.leading.trailing.mas_equalTo(self.view); make.top.mas_equalTo(self.view).offset(66 + 98 + 44 + kSafeAreaTopHeight + 20); }]; } else { UIImageView *topBg = [self topBG]; [self.view addSubview:topBg]; [topBg mas_makeConstraints:^(MASConstraintMaker *make) { make.top.leading.trailing.mas_equalTo(self.view); make.height.mas_equalTo(kGetScaleWidth(314)); }]; [bottomBg mas_makeConstraints:^(MASConstraintMaker *make) { make.bottom.leading.trailing.mas_equalTo(self.view); // make.height.mas_equalTo(kGetScaleWidth(445)); make.top.mas_equalTo(topBg.mas_bottom).offset(44); }]; } } - (void)setupOthersMedalInfo { UIImageView *bg = [[UIImageView alloc] initWithImage:kImage(@"medals_other_bg")]; [self.view addSubview:bg]; [bg mas_makeConstraints:^(MASConstraintMaker *make) { make.top.mas_equalTo(80); make.leading.trailing.mas_equalTo(self.view).inset(12); make.height.mas_equalTo(kGetScaleWidth(98)); }]; self.otherBG = bg; [self.view addSubview:self.otherAvatar]; [self.otherAvatar mas_makeConstraints:^(MASConstraintMaker *make) { make.centerY.mas_equalTo(bg); make.leading.mas_equalTo(bg).offset(15); make.size.mas_equalTo(CGSizeMake(53, 53)); }]; self.otherNameLabel = [UILabel labelInitWithText:@"" font:kFontSemibold(15) textColor:UIColorFromRGB(0x313131)]; [self.view addSubview:self.otherNameLabel]; [self.otherNameLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.mas_equalTo(self.otherAvatar.mas_trailing).offset(10); make.top.mas_equalTo(self.otherAvatar); }]; UIImageView *rankIcon = [[UIImageView alloc] initWithImage:kImage(@"medals_icon_other")]; [self.view addSubview:rankIcon]; [rankIcon mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.mas_equalTo(self.otherAvatar.mas_trailing).offset(10); make.bottom.mas_equalTo(self.otherAvatar); make.size.mas_equalTo(CGSizeMake(22, 22)); }]; self.otherCountLabel = [UILabel labelInitWithText:@"" font:kFontMedium(20) textColor:UIColorFromRGB(0x313131)]; [self.view addSubview:self.otherCountLabel]; [self.otherCountLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.mas_equalTo(rankIcon.mas_trailing).offset(6); make.centerY.mas_equalTo(rankIcon); }]; self.otherMedal = [[NetImageView alloc] initWithImage:kImage(@"medals_empty_other")]; [self.view addSubview:self.otherMedal]; [self.otherMedal mas_makeConstraints:^(MASConstraintMaker *make) { make.centerY.mas_equalTo(bg); make.trailing.mas_equalTo(bg).offset(-15); make.size.mas_equalTo(CGSizeMake(81, 81)); }]; [self.view addSubview:self.otherMP4View]; [self.otherMP4View mas_makeConstraints:^(MASConstraintMaker *make) { make.centerY.mas_equalTo(bg); make.trailing.mas_equalTo(bg).offset(-15); make.size.mas_equalTo(CGSizeMake(80, 80)); }]; } - (void)setupCenterTabs { self.centerTabsStackView = [self centerTabStack]; [self.view addSubview:self.centerTabsStackView]; [self.centerTabsStackView mas_makeConstraints:^(MASConstraintMaker *make) { if (self.displayType == MedalsCenterDisplayType_Other) { make.top.mas_equalTo(self.otherBG.mas_bottom).offset(18); } else { make.top.mas_equalTo(kGetScaleWidth(314)); } make.height.mas_equalTo(44); make.leading.trailing.mas_equalTo(self.view); }]; } - (void)setupWearingButton { UIView *wearingBg = [[UIView alloc] init]; [wearingBg addGradientBackgroundWithColors:@[ UIColorFromRGB(0xf2e7ff), UIColorFromRGB(0xb497f6) ] startPoint:CGPointMake(0, 0.5) endPoint:CGPointMake(1, 0.5) cornerRadius:25/2]; [wearingBg setAllCornerRadius:25/2 borderWidth:1 borderColor:[UIColor whiteColor]]; [self.view addSubview:wearingBg]; UIButton *wearingButton = [self wearingButton]; [self.view addSubview:wearingButton]; [wearingButton mas_makeConstraints:^(MASConstraintMaker *make) { make.top.mas_equalTo(90); make.trailing.mas_equalTo(-12); make.height.mas_equalTo(25); }]; [wearingBg mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.mas_equalTo(wearingButton).insets(UIEdgeInsetsMake(0, -25, 0, -25)); }]; } - (void)setupEmptyUserMedals { [self.view addSubview:self.medalsCyclePagerView]; [self.view addSubview:self.emptyUserMedalButton]; [self.emptyUserMedalButton mas_makeConstraints:^(MASConstraintMaker *make) { make.width.height.mas_equalTo(184); make.centerX.mas_equalTo(self.view); make.top.mas_equalTo(100); }]; [self.medalsCyclePagerView mas_makeConstraints:^(MASConstraintMaker *make) { make.center.mas_equalTo(self.emptyUserMedalButton); make.width.mas_equalTo([UIScreen mainScreen].bounds.size.width); // make.width.mas_equalTo(184*3); make.height.mas_equalTo(self.emptyUserMedalButton); }]; [self.view addSubview:self.medalDescLabel]; if (self.displayType != MedalsCenterDisplayType_Square) { self.medalDescLabel.text = YMLocalizedString(@"20.20.61_text_6"); } [self.medalDescLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.top.mas_equalTo(kGetScaleWidth(290)); make.leading.trailing.mas_equalTo(self.view).inset(20); make.height.mas_equalTo(20); }]; } - (void)setupWearingUserMedals { // // 添加 TYCyclePagerView 用于显示佩戴的勋章 // [self.view addSubview:self.medalsCyclePagerView]; // [self.medalsCyclePagerView mas_makeConstraints:^(MASConstraintMaker *make) { // make.top.mas_equalTo(100); // make.centerX.mas_equalTo(self.view); //// make.width.mas_equalTo([UIScreen mainScreen].bounds.size.width); // make.width.height.mas_equalTo(184); // }]; [self.view addSubview:self.medalDescLabel]; self.medalDescLabel.text = @"显示过期时间";//YMLocalizedString(@"20.20.61_text_6"); [self.medalDescLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.top.mas_equalTo(kGetScaleWidth(290)); make.leading.trailing.mas_equalTo(self.view).inset(20); make.height.mas_equalTo(20); }]; } - (void)setupScrollMedalsList { } - (void)setupBottomMedalsList { [self.view addSubview:self.medalsCollectionView]; [self.medalsCollectionView mas_makeConstraints:^(MASConstraintMaker *make) { make.bottom.mas_equalTo(self.view); make.trailing.leading.mas_equalTo(self.view).inset(26); make.top.mas_equalTo(self.centerTabsStackView.mas_bottom).offset(68); }]; // 添加下拉刷新 MJRefreshNormalHeader *header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(headerRefresh)]; header.lastUpdatedTimeLabel.hidden = YES; self.medalsCollectionView.mj_header = header; // 添加上拉加载更多 MJRefreshBackNormalFooter *footer = [MJRefreshBackNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(footerRefresh)]; self.medalsCollectionView.mj_footer = footer; } - (void)setupEmptyView { self.emptyView.hidden = YES; [self.view insertSubview:self.emptyView belowSubview:self.medalsCollectionView]; [self.emptyView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.leading.trailing.mas_equalTo(self.medalsCollectionView); make.height.mas_equalTo(200); }]; } #pragma mark - Refresh Methods - (void)headerRefresh { NSInteger page = 1; switch (self.currentTabType) { case MedalsCenterTab_TaskMedals: self.currentPageTaskMedals = 1; page = self.currentPageTaskMedals; break; case MedalsCenterTab_ActivityMedals: self.currentPageActivityMedals = 1; page = self.currentPageActivityMedals; break; case MedalsCenterTab_GloryMedals: self.currentPageGloryMedals = 1; page = self.currentPageGloryMedals; break; default: break; } [self loadMedalsList:self.currentTabType page:page]; } - (void)footerRefresh { NSInteger page = 1; switch (self.currentTabType) { case MedalsCenterTab_TaskMedals: self.currentPageTaskMedals += 1; page = self.currentPageTaskMedals; break; case MedalsCenterTab_ActivityMedals: self.currentPageActivityMedals += 1; page = self.currentPageActivityMedals; break; case MedalsCenterTab_GloryMedals: self.currentPageGloryMedals += 1; page = self.currentPageGloryMedals; break; default: break; } [self loadMedalsList:self.currentTabType page:page]; } - (void)endReresh { [self.medalsCollectionView.mj_header endRefreshing]; [self.medalsCollectionView.mj_footer endRefreshing]; } #pragma mark - Load datas - (void)setupData { self.datasourceTaskMedals = @[].mutableCopy; self.datasourceActivityMedals = @[].mutableCopy; self.datasourceGloryMedals = @[].mutableCopy; self.currentPageTaskMedals = 1; self.currentPageActivityMedals = 1; self.currentPageGloryMedals = 1; self.currentTabType = MedalsCenterTab_TaskMedals; [self loadMedalsList:self.currentTabType page:1]; } - (void)loadMedalsList:(MedalsCenterTabType)tabType page:(NSInteger)tabPage { if (self.displayType == MedalsCenterDisplayType_Square) { [self.presenter squareMedals:tabPage type:tabType]; } else { [self.presenter userMedals:self.userInfo.uid page:tabPage type:tabType]; } } #pragma mark - MedalsPresenterProtocol - (void)userMedalsSuccess:(UserMedalsModel *)userMedalsModel { self.userMedalsModel = userMedalsModel; [self endReresh]; [self _updateDataSource:userMedalsModel.medalSeries]; [self _updateOtherInfo:userMedalsModel]; [self _updateWearingInfo]; } - (void)userMedalsFailure { [self endReresh]; // 如果是加载更多失败,页码需要回退 switch (self.currentTabType) { case MedalsCenterTab_TaskMedals: if (self.currentPageTaskMedals > 1) { self.currentPageTaskMedals--; } break; case MedalsCenterTab_ActivityMedals: if (self.currentPageActivityMedals > 1) { self.currentPageActivityMedals--; } break; case MedalsCenterTab_GloryMedals: if (self.currentPageGloryMedals > 1) { self.currentPageGloryMedals--; } break; default: break; } [self _updateDataSource:@[]]; } - (void)squareMedalsSuccess:(NSArray *)squareMedalsModel { switch (self.currentTabType) { case MedalsCenterTab_TaskMedals: if (self.currentPageTaskMedals == 1) { self.taskSquareMedalVo = squareMedalsModel.mutableCopy; } else { [self.taskSquareMedalVo addObjectsFromArray:squareMedalsModel]; } break; case MedalsCenterTab_ActivityMedals: if (self.currentPageActivityMedals == 1) { self.activitySquareMedalVo = squareMedalsModel.mutableCopy; } else { [self.activitySquareMedalVo addObjectsFromArray:squareMedalsModel]; } break; case MedalsCenterTab_GloryMedals: if (self.currentPageGloryMedals == 1) { self.glorySquareMedalVo = squareMedalsModel.mutableCopy; } else { [self.glorySquareMedalVo addObjectsFromArray:squareMedalsModel]; } break; default: break; } [self endReresh]; [self _updateDataSource:squareMedalsModel]; [self _updateWearingInfo]; } - (void)squareMedalsFailure { [self endReresh]; // 如果是加载更多失败,页码需要回退 switch (self.currentTabType) { case MedalsCenterTab_TaskMedals: if (self.currentPageTaskMedals > 1) { self.currentPageTaskMedals--; } break; case MedalsCenterTab_ActivityMedals: if (self.currentPageActivityMedals > 1) { self.currentPageActivityMedals--; } break; case MedalsCenterTab_GloryMedals: if (self.currentPageGloryMedals > 1) { self.currentPageGloryMedals--; } break; default: break; } } - (void)_updateWearingInfo { if (self.displayType == MedalsCenterDisplayType_Mine) { self.useMedals = self.userMedalsModel.useMedals; } else if (self.displayType == MedalsCenterDisplayType_Square) { NSArray *arr = [NSArray array]; NSMutableArray *editArr = [NSMutableArray array]; switch (self.currentTabType) { case MedalsCenterTab_TaskMedals: arr = self.taskSquareMedalVo.copy; break; case MedalsCenterTab_ActivityMedals: arr = self.activitySquareMedalVo.copy; break; case MedalsCenterTab_GloryMedals: arr = self.glorySquareMedalVo.copy; break; default: break; } for (MedalSeriesVo *seriesVO in arr) { for (MedalSeriesItemVo *item in seriesVO.medalSeries) { // 遍历所有medalSeries,找到最高级别的 for (MedalVo *medalVo in item.medalVos) { if (medalVo.level == item.medalLevel) { [editArr addObject:medalVo]; } } } } self.useMedals = editArr.copy; } if (self.displayType != MedalsCenterDisplayType_Other) { if (self.useMedals.count > 0 ) { self.emptyUserMedalButton.hidden = YES; self.medalsCyclePagerView.hidden = NO; [self.medalsCyclePagerView reloadData]; if (self.useMedals.count > 1 && self.displayType == MedalsCenterDisplayType_Square) { // 启动自动轮播,从第一个位置开始 [self startAutoScroll:0]; // 确保滚动到第一个位置 dispatch_async(dispatch_get_main_queue(), ^{ [self.medalsCyclePagerView scrollToItemAtIndex:0 animate:NO]; }); } else { // 停止自动轮播 [self stopAutoScroll]; } } else { self.emptyUserMedalButton.hidden = NO; self.medalsCyclePagerView.hidden = YES; } } } - (void)startVisibleCellsPlayback { // 获取所有可见的 cell 并启动播放 NSArray *visibleCells = self.medalsCyclePagerView.collectionView.visibleCells; for (UICollectionViewCell *cell in visibleCells) { if ([cell isKindOfClass:[MedalsCyclePagerCell class]]) { [(MedalsCyclePagerCell *)cell willDisplay]; } } } - (void)_updateOtherInfo:(UserMedalsModel *)userModel { _otherMedalsModel = userModel; if (self.displayType == MedalsCenterDisplayType_Other) { self.otherAvatar.imageUrl = userModel.avatar; self.otherNameLabel.text = userModel.nick; self.otherCountLabel.text = @(userModel.medalNum).stringValue; MedalVo *useMedal = [[userModel useMedals] xpSafeObjectAtIndex:0]; if (useMedal) { if ([useMedal.picUrl hasSuffix:@"mp4"]) { // 显示客态的 MP4 播放 [self displayOtherMedalMp4:useMedal.picUrl]; } else { // 显示客态的 图片 [self displayOtherMedalImage:useMedal.picUrl]; } } else { // 没有勋章时显示默认图片 [self displayOtherMedalImage:nil]; } } } #pragma mark - 其他用户勋章显示方法 /** * 显示其他用户的勋章图片 * @param imageUrl 图片URL,如果为nil则显示默认图片 */ - (void)displayOtherMedalImage:(NSString *)imageUrl { // 停止MP4播放 [self stopOtherMedalMP4Playback]; // 显示图片,隐藏MP4视图 self.otherMP4View.hidden = YES; self.otherMedal.hidden = NO; if (imageUrl && imageUrl.length > 0) { self.otherMedal.imageUrl = imageUrl; } else { // 显示默认空勋章图片 self.otherMedal.image = kImage(@"medals_empty_other"); } } /** * 显示其他用户的勋章MP4动画 * @param mp4Url MP4文件URL */ - (void)displayOtherMedalMp4:(NSString *)mp4Url { if (!mp4Url || mp4Url.length == 0) { [self displayOtherMedalImage:nil]; return; } // 停止之前的MP4播放 [self stopOtherMedalMP4Playback]; // 显示MP4视图,隐藏图片 self.otherMP4View.hidden = NO; self.otherMedal.hidden = YES; // 初始化MP4解析器 if (!self.mp4Parser) { self.mp4Parser = [[XPRoomGiftAnimationParser alloc] init]; } @kWeakify(self); [self.mp4Parser parseWithURL:mp4Url completionBlock:^(NSString * _Nullable videoUrl) { @kStrongify(self); if (![NSString isEmpty:videoUrl] && !self.otherMP4View.hidden) { // 静音播放,因为是展示其他用户的勋章 [self.otherMP4View setMute:YES]; [self.otherMP4View playHWDMP4:videoUrl repeatCount:-1 delegate:nil]; } } failureBlock:^(NSError * _Nullable error) { @kStrongify(self); NSLog(@"Failed to parse other user medal mp4: %@", error); // 播放失败时显示图片 [self displayOtherMedalImage:nil]; }]; } /** * 停止其他用户勋章的MP4播放 */ - (void)stopOtherMedalMP4Playback { if (self.otherMP4View) { [self.otherMP4View stopHWDMP4]; } } - (void)_updateDataSource:(NSArray *)models { NSArray *targetArr = models; NSMutableArray *itemDataSources = [NSMutableArray array]; MedalSeriesVo *series = [models xpSafeObjectAtIndex:0]; if (series) { targetArr = series.medalSeries; } itemDataSources = targetArr.mutableCopy; if (targetArr.count < [self.presenter pageSize]) { [self.medalsCollectionView.mj_footer endRefreshingWithNoMoreData]; } else { [self.medalsCollectionView.mj_footer resetNoMoreData]; } switch (self.currentTabType) { case MedalsCenterTab_TaskMedals: if (self.currentPageTaskMedals == 1) { self.emptyView.hidden = (targetArr.count != 0); self.datasourceTaskMedals = itemDataSources; } else { [self.datasourceTaskMedals addObjectsFromArray:itemDataSources]; } break; case MedalsCenterTab_ActivityMedals: if (self.currentPageActivityMedals == 1) { self.emptyView.hidden = (targetArr.count != 0); self.datasourceActivityMedals = itemDataSources; } else { [self.datasourceActivityMedals addObjectsFromArray:itemDataSources]; } break; case MedalsCenterTab_GloryMedals: if (self.currentPageGloryMedals == 1) { self.emptyView.hidden = (targetArr.count != 0); self.datasourceGloryMedals = itemDataSources; } else { [self.datasourceGloryMedals addObjectsFromArray:itemDataSources]; } break; default: break; } [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]; [self.navigationController pushViewController:vc animated:YES]; } - (void)didTapRankButton:(UIButton *)sender { MedalsRankViewController *vc = [[MedalsRankViewController alloc] init]; [self.navigationController pushViewController:vc animated:YES]; } - (void)didTapWearingButton:(UIButton *)sender { MedalsWearingViewController *vc = [[MedalsWearingViewController alloc] init]; vc.userInfo = self.userInfo; // 设置数据变化回调 @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]; vc.userInfo = self.userInfo; // 设置数据变化回调 @kWeakify(self); vc.dataChangedCallback = ^{ @kStrongify(self); // 刷新数据 [self refreshDataAfterWearingChange]; }; [self addChildViewController:vc]; [self.view addSubview:vc.view]; } - (void)didTapCenterTab:(UIButton *)sender { if (sender.isSelected == YES) { return; } for (UIButton *b in self.centerTabButtons) { b.selected = NO; } sender.selected = YES; self.currentTabType = sender.tag; // 切换 tab 时重置自动滚动 [self resetAutoScrollOnTabChange]; [self loadMedalsList:self.currentTabType page:1]; } #pragma mark - UICollectionView DataSource & Delegate - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { switch (self.currentTabType) { case MedalsCenterTab_TaskMedals: return self.datasourceTaskMedals.count; break; case MedalsCenterTab_ActivityMedals: return self.datasourceActivityMedals.count; break; case MedalsCenterTab_GloryMedals: return self.datasourceGloryMedals.count; break; default: break; } } - (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { MedalsCollectionViewCell *cell = [MedalsCollectionViewCell cellFor:collectionView atIndexPath:indexPath]; [cell updateCell:[self loadModel:indexPath.item] isForSquare:self.displayType == MedalsCenterDisplayType_Square isForMine:self.displayType == MedalsCenterDisplayType_Mine]; return cell; } // 处理 cell 的可见性 - (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath { if (collectionView == self.medalsCollectionView) { if ([cell isKindOfClass:[MedalsCollectionViewCell class]]) { [(MedalsCollectionViewCell *)cell willDisplay]; } } } - (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath { if (collectionView == self.medalsCollectionView) { if ([cell isKindOfClass:[MedalsCollectionViewCell class]]) { [(MedalsCollectionViewCell *)cell didEndDisplaying]; } } } - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { if (collectionView == self.medalsCyclePagerView.collectionView) { return; } MedalsDetailView *view = [[MedalsDetailView alloc] initWithFrame:self.view.bounds]; [view updateSeriesItem:[self loadModel:indexPath.item] isMine:self.displayType == MedalsCenterDisplayType_Mine || self.displayType == MedalsCenterDisplayType_Other]; [self.view addSubview:view]; } - (MedalSeriesItemVo *)loadModel:(NSInteger)row { MedalSeriesItemVo *model = nil; switch (self.currentTabType) { case MedalsCenterTab_TaskMedals: model = [self.datasourceTaskMedals xpSafeObjectAtIndex:row]; break; case MedalsCenterTab_ActivityMedals: model = [self.datasourceActivityMedals xpSafeObjectAtIndex:row]; break; case MedalsCenterTab_GloryMedals: model = [self.datasourceGloryMedals xpSafeObjectAtIndex:row]; break; default: break; } return model; } - (MedalSeriesVo *)loadDetailModel:(NSInteger)row { MedalSeriesVo *model = nil; switch (self.currentTabType) { case MedalsCenterTab_TaskMedals: model = [self.taskSquareMedalVo xpSafeObjectAtIndex:row]; break; case MedalsCenterTab_ActivityMedals: model = [self.activitySquareMedalVo xpSafeObjectAtIndex:row]; break; case MedalsCenterTab_GloryMedals: model = [self.glorySquareMedalVo xpSafeObjectAtIndex:row]; break; default: break; } return model; } #pragma mark - TYCyclePagerView DataSource & Delegate - (NSInteger)numberOfItemsInPagerView:(TYCyclePagerView *)pageView { return self.useMedals.count; } - (__kindof UICollectionViewCell *)pagerView:(TYCyclePagerView *)pagerView cellForItemAtIndex:(NSInteger)index { MedalsCyclePagerCell *cell = [pagerView dequeueReusableCellWithReuseIdentifier:@"MedalsCyclePagerCell" forIndex:index]; MedalVo *vo = [self.useMedals xpSafeObjectAtIndex:index]; [cell updateCell:vo]; return cell; } - (TYCyclePagerViewLayout *)layoutForPagerView:(TYCyclePagerView *)pageView { TYCyclePagerViewLayout *layout = [[TYCyclePagerViewLayout alloc] init]; layout.itemSize = CGSizeMake(184, 184); layout.itemSpacing = 10; layout.layoutType = TYCyclePagerTransformLayoutLinear; layout.itemHorizontalCenter = YES; return layout; } - (void)pagerView:(TYCyclePagerView *)pageView didScrollFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex { // 用户手动滚动时,更新当前索引,重新开始自动轮播计时 self.currentAutoScrollIndex = toIndex; // 如果有多个勋章且在广场页面,重新启动自动轮播定时器 if (self.useMedals.count > 1 && self.displayType == MedalsCenterDisplayType_Square) { [self startAutoScroll:toIndex]; } if (self.displayType == MedalsCenterDisplayType_Mine) { MedalVo *vo = [self.useMedals xpSafeObjectAtIndex:toIndex]; self.medalDescLabel.text = [vo expireDateString]; } else { self.medalDescLabel.text = @""; } } #pragma mark - Lazy load - (UIButton *)medalsSquareButton { UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; [button addTarget:self action:@selector(didTapSquareButton:) forControlEvents:UIControlEventTouchUpInside]; [button setBackgroundImage:kImage(@"medals_square") forState:UIControlStateNormal]; return button; } - (UIButton *)medalsRankButton { UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; [button addTarget:self action:@selector(didTapRankButton:) forControlEvents:UIControlEventTouchUpInside]; [button setBackgroundImage:kImage(@"medals_rank") forState:UIControlStateNormal]; return button; } - (UIButton *)wearingButton { UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; [button addTarget:self action:@selector(didTapWearingButton:) forControlEvents:UIControlEventTouchUpInside]; [button setTitle:YMLocalizedString(@"20.20.61_text_2") forState:UIControlStateNormal]; [button setTitleColor:UIColorFromRGB(0x201440) forState:UIControlStateNormal]; button.titleLabel.font = kFontMedium(12); return button; } - (UIImageView *)topBG { UIImageView *iv = [[UIImageView alloc] initWithImage:kImage(@"medals_top_bg")]; iv.contentMode = UIViewContentModeScaleAspectFit; return iv; } - (UIImageView *)bottomBG { UIImage *originalImage = kImage(@"medals_bottom_bg"); // 创建可拉伸的图片,让下半部分可以拉伸 // 假设图片高度的上半部分不拉伸,下半部分可拉伸 CGFloat imageHeight = originalImage.size.height; CGFloat stretchableTopInset = imageHeight * 0.5; // 上半部分不拉伸 CGFloat stretchableBottomInset = 1; // 下半部分保留1像素不拉伸 UIImage *stretchableImage = [originalImage resizableImageWithCapInsets:UIEdgeInsetsMake(stretchableTopInset, 0, stretchableBottomInset, 0) resizingMode:UIImageResizingModeStretch]; UIImageView *iv = [[UIImageView alloc] initWithImage:stretchableImage]; iv.contentMode = UIViewContentModeScaleToFill; // 使用ScaleToFill来应用拉伸效果 return iv; } - (UIButton *)taskMedalsButton:(MedalsCenterTabType)tabType { UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; button.tag = tabType; switch (tabType) { case MedalsCenterTab_TaskMedals: [button setTitle:YMLocalizedString(@"20.20.61_text_3") forState:UIControlStateNormal]; break; case MedalsCenterTab_ActivityMedals: [button setTitle:YMLocalizedString(@"20.20.61_text_4") forState:UIControlStateNormal]; break; case MedalsCenterTab_GloryMedals: [button setTitle:YMLocalizedString(@"20.20.61_text_5") forState:UIControlStateNormal]; break; default: break; } [button addTarget:self action:@selector(didTapCenterTab:) forControlEvents:UIControlEventTouchUpInside]; [button setBackgroundImage:kImage(@"medals_tab_normal") forState:UIControlStateNormal]; [button setBackgroundImage:kImage(@"medals_tab_selected") forState:UIControlStateSelected]; [button setTitleColor:UIColorFromRGB(0xd8a2ff) forState:UIControlStateNormal]; [button setTitleColor:UIColorFromRGB(0xFFFFFF) forState:UIControlStateSelected]; [button.titleLabel setFont:kFontMedium(13)]; [button.titleLabel setAdjustsFontSizeToFitWidth:YES]; button.contentEdgeInsets = UIEdgeInsetsMake(0, 20, 0, 20); [button setAdjustsImageWhenDisabled:NO]; return button; } - (UIStackView *)centerTabStack { UIButton *taskMedalsButton = [self taskMedalsButton:MedalsCenterTab_TaskMedals]; taskMedalsButton.selected = YES; UIButton *activityMedalsButton = [self taskMedalsButton:MedalsCenterTab_ActivityMedals]; UIButton *gloryMedalsButton = [self taskMedalsButton:MedalsCenterTab_GloryMedals]; self.centerTabButtons = @[ taskMedalsButton, activityMedalsButton, gloryMedalsButton ]; UIStackView *stack = [[UIStackView alloc] initWithArrangedSubviews:self.centerTabButtons]; stack.distribution = UIStackViewDistributionFillEqually; return stack; } - (UILabel *)medalDescLabel { if (!_medalDescLabel) { _medalDescLabel = [UILabel labelInitWithText:@"" font:kFontRegular(12) textColor:[UIColor whiteColor]]; _medalDescLabel.textAlignment = NSTextAlignmentCenter; } return _medalDescLabel; } - (UIButton *)emptyUserMedalButton { if (!_emptyUserMedalButton) { _emptyUserMedalButton = [UIButton buttonWithType:UIButtonTypeCustom]; [_emptyUserMedalButton setImage:kImage(@"medals_empty") forState:UIControlStateNormal]; [_emptyUserMedalButton addTarget:self action:@selector(didTapEmptyMedalButton:) forControlEvents:UIControlEventTouchUpInside]; } return _emptyUserMedalButton; } - (UICollectionView *)medalsCollectionView { if (!_medalsCollectionView) { UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; layout.itemSize = CGSizeMake(kGetScaleWidth(156), kGetScaleWidth(218)); layout.minimumInteritemSpacing = 6; layout.minimumLineSpacing = 6; _medalsCollectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; _medalsCollectionView.backgroundColor = [UIColor clearColor]; _medalsCollectionView.delegate = self; _medalsCollectionView.dataSource = self; [MedalsCollectionViewCell registerTo:_medalsCollectionView]; } return _medalsCollectionView; } - (UIView *)emptyView { if (!_emptyView) { _emptyView = [[UIView alloc] init]; _emptyView.backgroundColor = [UIColor clearColor]; NSString *content = YMLocalizedString(@"20.20.61_text_7.1"); if (self.displayType == MedalsCenterDisplayType_Mine) { content = YMLocalizedString(@"20.20.61_text_7.2"); } UILabel *titleLabel = [UILabel labelInitWithText:content font:kFontRegular(14) textColor:UIColorFromRGB(0xafb1b3)]; titleLabel.numberOfLines = 2; titleLabel.textAlignment = NSTextAlignmentCenter; UIImageView *ufoImageView = [[UIImageView alloc] initWithImage:kImage(@"common_empty_UFO")]; [_emptyView addSubview:ufoImageView]; [_emptyView addSubview:titleLabel]; [ufoImageView mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.mas_equalTo(_emptyView); make.top.mas_equalTo(20); make.size.mas_equalTo(CGSizeMake(110, 110)); }]; [titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.mas_equalTo(_emptyView); make.top.mas_equalTo(ufoImageView.mas_bottom).offset(10); make.leading.trailing.mas_equalTo(_emptyView).inset(20); }]; } return _emptyView; } - (NetImageView *)otherAvatar { if (!_otherAvatar) { NetImageConfig *config = [[NetImageConfig alloc] init]; config.placeHolder = [UIImageConstant defaultAvatarPlaceholder]; _otherAvatar = [[NetImageView alloc] initWithConfig:config]; [_otherAvatar setAllCornerRadius:53/2 borderWidth:1 borderColor:[UIColor whiteColor]]; } return _otherAvatar; } - (TYCyclePagerView *)medalsCyclePagerView { if (!_medalsCyclePagerView) { _medalsCyclePagerView = [[TYCyclePagerView alloc] init]; _medalsCyclePagerView.dataSource = self; _medalsCyclePagerView.delegate = self; _medalsCyclePagerView.backgroundColor = [UIColor clearColor]; _medalsCyclePagerView.isInfiniteLoop = NO; _medalsCyclePagerView.clipsToBounds = NO; // _medalsCyclePagerView.autoScrollInterval = 0; // 禁用自动滚动 [_medalsCyclePagerView registerClass:[MedalsCyclePagerCell class] forCellWithReuseIdentifier:@"MedalsCyclePagerCell"]; // 设置内部 collectionView 的代理,用于处理 cell 的显示和隐藏 // _medalsCyclePagerView.collectionView.delegate = self; } return _medalsCyclePagerView; } - (VAPView *)otherMP4View { if (!_otherMP4View) { _otherMP4View = [[VAPView alloc] init]; _otherMP4View.contentMode = UIViewContentModeScaleAspectFit; } return _otherMP4View; } @end