From 8c024c0ec18e9c0bcf7220c9f83aae238b92045d Mon Sep 17 00:00:00 2001 From: edwinQQQ Date: Fri, 29 Aug 2025 15:55:02 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=20XPRoomViewController=20?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E8=A7=86=E5=9B=BE=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=8C=E7=A7=BB=E9=99=A4=20stageView=20?= =?UTF-8?q?=E7=9A=84=E6=B7=BB=E5=8A=A0=EF=BC=8C=E8=B0=83=E6=95=B4=20messag?= =?UTF-8?q?eContainerView=20=E7=9A=84=E7=BA=A6=E6=9D=9F=E3=80=82=E5=90=8C?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E4=BF=AE=E5=A4=8D=20BravoGiftWinningFlagView?= =?UTF-8?q?=20=E5=92=8C=20LuckyGiftWinningFlagView=20=E7=9A=84=E4=BD=8D?= =?UTF-8?q?=E7=BD=AE=E8=AE=A1=E7=AE=97=EF=BC=8C=E7=A1=AE=E4=BF=9D=E8=A7=86?= =?UTF-8?q?=E5=9B=BE=E5=9C=A8=E5=B1=8F=E5=B9=95=E5=8F=AF=E8=A7=81=E8=8C=83?= =?UTF-8?q?=E5=9B=B4=E5=86=85=EF=BC=8C=E5=A2=9E=E5=BC=BA=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BD=93=E9=AA=8C=E3=80=82=E6=96=B0=E5=A2=9E=E6=99=BA=E8=83=BD?= =?UTF-8?q?=E9=94=80=E6=AF=81=E6=96=B9=E6=B3=95=E4=BB=A5=E4=BC=98=E5=8C=96?= =?UTF-8?q?=20RoomAnimationView=20=E7=9A=84=E8=B5=84=E6=BA=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=EF=BC=8C=E7=A1=AE=E4=BF=9D=E9=87=8D=E8=A6=81=E5=8A=A8?= =?UTF-8?q?=E7=94=BB=E6=92=AD=E6=94=BE=E6=97=B6=E7=9A=84=E6=B8=85=E7=90=86?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E6=9B=B4=E5=8A=A0=E5=AE=89=E5=85=A8=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AnimationView/BravoGiftWinningFlagView.m | 23 ++- .../AnimationView/LuckyGiftWinningFlagView.m | 21 ++- .../View/AnimationView/RoomAnimationView.m | 177 +++++++++++++----- .../YMRoom/View/XPRoomViewController.m | 16 +- 4 files changed, 177 insertions(+), 60 deletions(-) diff --git a/YuMi/Modules/YMRoom/View/AnimationView/BravoGiftWinningFlagView.m b/YuMi/Modules/YMRoom/View/AnimationView/BravoGiftWinningFlagView.m index 2ec781df..4f52f2b3 100644 --- a/YuMi/Modules/YMRoom/View/AnimationView/BravoGiftWinningFlagView.m +++ b/YuMi/Modules/YMRoom/View/AnimationView/BravoGiftWinningFlagView.m @@ -30,7 +30,7 @@ + (BravoGiftWinningFlagViewModel *)display:(UIView *)superView with:(AttachmentModel *)attachment roomID:(NSInteger)roomID uID:(NSString *)UID { BravoGiftWinningFlagViewModel *model = [BravoGiftWinningFlagViewModel modelWithJSON:attachment.data]; - if (model.roomId != roomID || model.uid != UID.integerValue || model.tip == nil) { + if (model.roomId != roomID || model.uid != UID.integerValue || !model.tip) { return model; } @@ -38,8 +38,27 @@ flagView.model = model; flagView.alpha = 0; flagView.frame = CGRectMake(0, 0, kGetScaleWidth(274), kGetScaleWidth(216)); - flagView.center = CGPointMake(superView.center.x, kGetScaleWidth(216) + kGetScaleWidth(216)/2 - 40); + + // 🔧 修复:重新计算位置,确保视图在屏幕可见范围内 + CGFloat viewWidth = kGetScaleWidth(274); + CGFloat viewHeight = kGetScaleWidth(216); + CGFloat screenWidth = KScreenWidth; + CGFloat screenHeight = KScreenHeight; + + // 计算安全的 Y 位置:屏幕高度的 40% 位置 + CGFloat safeY = screenHeight * 0.4; + + // 确保视图不会超出屏幕边界 + CGFloat maxY = screenHeight - viewHeight/2 - 20; // 留20点边距 + CGFloat minY = viewHeight/2 + 20; // 留20点边距 + CGFloat finalY = MAX(minY, MIN(safeY, maxY)); + + flagView.center = CGPointMake(screenWidth/2, finalY); flagView.transform = CGAffineTransformMakeScale(0.1, 0.1); + + NSLog(@"🎯 BravoGiftWinningFlagView: 位置计算 - screenSize: %.0fx%.0f, viewSize: %.0fx%.0f, center: (%.0f, %.0f)", + screenWidth, screenHeight, viewWidth, viewHeight, flagView.center.x, flagView.center.y); + [superView addSubview:flagView]; // 使用弹簧动画执行放大动画,alpha从0变为1,带有弹性效果 diff --git a/YuMi/Modules/YMRoom/View/AnimationView/LuckyGiftWinningFlagView.m b/YuMi/Modules/YMRoom/View/AnimationView/LuckyGiftWinningFlagView.m index 878537fe..c4d5bde9 100644 --- a/YuMi/Modules/YMRoom/View/AnimationView/LuckyGiftWinningFlagView.m +++ b/YuMi/Modules/YMRoom/View/AnimationView/LuckyGiftWinningFlagView.m @@ -54,8 +54,27 @@ winningFlagView.model = model; winningFlagView.alpha = 0; winningFlagView.frame = CGRectMake(0, 0, kGetScaleWidth(162), kGetScaleWidth(162)); - winningFlagView.center = CGPointMake(superView.center.x, kGetScaleWidth(163) + kGetScaleWidth(162)/2); + + // 🔧 修复:重新计算位置,确保视图在屏幕可见范围内 + CGFloat viewWidth = kGetScaleWidth(162); + CGFloat viewHeight = kGetScaleWidth(162); + CGFloat screenWidth = KScreenWidth; + CGFloat screenHeight = KScreenHeight; + + // 计算安全的 Y 位置:屏幕高度的 35% 位置 + CGFloat safeY = screenHeight * 0.35; + + // 确保视图不会超出屏幕边界 + CGFloat maxY = screenHeight - viewHeight/2 - 20; // 留20点边距 + CGFloat minY = viewHeight/2 + 20; // 留20点边距 + CGFloat finalY = MAX(minY, MIN(safeY, maxY)); + + winningFlagView.center = CGPointMake(screenWidth/2, finalY); winningFlagView.transform = CGAffineTransformMakeScale(0.1, 0.1); + + NSLog(@"🎯 LuckyGiftWinningFlagView: 位置计算 - screenSize: %.0fx%.0f, viewSize: %.0fx%.0f, center: (%.0f, %.0f)", + screenWidth, screenHeight, viewWidth, viewHeight, winningFlagView.center.x, winningFlagView.center.y); + [superView addSubview:winningFlagView]; // 使用弹簧动画执行放大动画,alpha从0变为1,带有弹性效果 diff --git a/YuMi/Modules/YMRoom/View/AnimationView/RoomAnimationView.m b/YuMi/Modules/YMRoom/View/AnimationView/RoomAnimationView.m index cf1aa599..753c494b 100644 --- a/YuMi/Modules/YMRoom/View/AnimationView/RoomAnimationView.m +++ b/YuMi/Modules/YMRoom/View/AnimationView/RoomAnimationView.m @@ -200,55 +200,29 @@ BannerSchedulerDelegate - (void)removeItSelf { NSLog(@"�� RoomAnimationView: 开始销毁"); - - // 🔧 新增:取消所有动画,防止异步回调 - [self cancelAllAnimations]; - - // 移除广播代理 - [[NIMSDK sharedSDK].broadcastManager removeDelegate:self]; - - // 取消所有延迟执行 - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - - // 清理所有 POP 动画 - [self pop_removeAllAnimations]; - - // 清理定时器 - if (_giftEffectTimer && !dispatch_source_testcancel(_giftEffectTimer)) { - dispatch_source_cancel(_giftEffectTimer); - _giftEffectTimer = nil; - } - - // 清理队列 - if (_giftEffectsQueue) { - self.giftEffectsQueue = NULL; - } - - // �� 新增:清理所有 banner 视图,防止 block 强引用 - [self cleanupAllSubviews]; - - // 🔧 新增:清理 BannerScheduler - if (self.bannerScheduler) { - NSLog(@"�� 清理 BannerScheduler"); - [self.bannerScheduler clearQueue]; - [self.bannerScheduler pause]; - self.bannerScheduler = nil; - } - - // 清理手势识别器 - [self cleanupGestureRecognizers]; - - // 清理缓存管理器 - [self cleanupCacheManagers]; - - // 移除通知监听 - [self removeNotificationObservers]; - - NSLog(@"�� RoomAnimationView: 销毁完成"); + [self smartDestroy]; } - (void)cancelAllAnimations { - NSLog(@" 取消所有动画"); + NSLog(@"�� 取消所有动画"); + + // �� 新增:检查是否有正在播放的重要动画 + BOOL hasImportantAnimation = [self hasImportantAnimationPlaying]; + + if (hasImportantAnimation) { + NSLog(@"⚠️ 检测到重要动画正在播放,延迟清理"); + // 延迟清理,给动画完成的时间 + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self forceCancelAllAnimations]; + }); + return; + } + + [self forceCancelAllAnimations]; +} + +- (void)forceCancelAllAnimations { + NSLog(@"🔄 强制取消所有动画"); // 取消所有 POP 动画 [self pop_removeAllAnimations]; @@ -269,7 +243,25 @@ BannerSchedulerDelegate } - (void)cleanupAllSubviews { - NSLog(@" 清理所有子视图"); + NSLog(@"🔄 清理所有子视图"); + + // �� 新增:检查是否有正在播放的重要动画 + BOOL hasImportantAnimation = [self hasImportantAnimationPlaying]; + + if (hasImportantAnimation) { + NSLog(@"⚠️ 检测到重要动画正在播放,延迟清理子视图"); + // 延迟清理,给动画完成的时间 + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self forceCleanupAllSubviews]; + }); + return; + } + + [self forceCleanupAllSubviews]; +} + +- (void)forceCleanupAllSubviews { + NSLog(@"�� 强制清理所有子视图"); // 清理所有容器的子视图 NSArray *containers = @[self.bannerContainer, self.topContainer, self.middleContainer, self.bottomContainer]; @@ -280,7 +272,7 @@ BannerSchedulerDelegate NSMutableArray *viewsToRemove = [NSMutableArray array]; for (UIView *subview in container.subviews) { [viewsToRemove addObject:subview]; - NSLog(@" 标记移除视图: %@ (从容器: %@)", NSStringFromClass([subview class]), NSStringFromClass([container class])); + NSLog(@"�� 标记移除视图: %@ (从容器: %@)", NSStringFromClass([subview class]), NSStringFromClass([container class])); } for (UIView *view in viewsToRemove) { @@ -289,7 +281,7 @@ BannerSchedulerDelegate // 清理视图上的手势识别器 if (view.gestureRecognizers.count > 0) { - NSLog(@" 清理视图 %@ 上的 %lu 个手势识别器", NSStringFromClass([view class]), (unsigned long)view.gestureRecognizers.count); + NSLog(@"�� 清理视图 %@ 上的 %lu 个手势识别器", NSStringFromClass([view class]), (unsigned long)view.gestureRecognizers.count); for (UIGestureRecognizer *gesture in view.gestureRecognizers.copy) { [view removeGestureRecognizer:gesture]; } @@ -4031,4 +4023,91 @@ BannerSchedulerDelegate } } +// 🔧 新增:检查重要动画状态 +- (BOOL)hasImportantAnimationPlaying { + // 检查 topContainer 中是否有正在播放的重要动画 + for (UIView *subview in self.topContainer.subviews) { + if ([subview isKindOfClass:[BravoGiftWinningFlagView class]] || + [subview isKindOfClass:[LuckyGiftWinningFlagView class]]) { + NSLog(@"🎯 检测到重要动画正在播放: %@", NSStringFromClass([subview class])); + return YES; + } + } + + // 检查 middleContainer 中是否有正在播放的礼物动画 + for (UIView *subview in self.middleContainer.subviews) { + if ([subview isKindOfClass:[SVGAImageView class]] || + [subview isKindOfClass:[VAPView class]] || + [subview isKindOfClass:[PAGView class]]) { + NSLog(@"🎯 检测到礼物动画正在播放: %@", NSStringFromClass([subview class])); + return YES; + } + } + + return NO; +} + +// �� 新增:智能销毁方法 +- (void)smartDestroy { + // 检查是否有正在播放的重要动画 + BOOL hasImportantAnimation = [self hasImportantAnimationPlaying]; + + if (hasImportantAnimation) { + NSLog(@"⚠️ 检测到重要动画正在播放,延迟销毁"); + // 延迟销毁,给动画完成的时间 + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self forceDestroy]; + }); + return; + } + + [self forceDestroy]; +} + +- (void)forceDestroy { + NSLog(@"�� RoomAnimationView: 强制销毁"); + + // 移除广播代理 + [[NIMSDK sharedSDK].broadcastManager removeDelegate:self]; + + // 取消所有延迟执行 + [NSObject cancelPreviousPerformRequestsWithTarget:self]; + + // 清理所有 POP 动画 + [self pop_removeAllAnimations]; + + // 清理定时器 + if (_giftEffectTimer && !dispatch_source_testcancel(_giftEffectTimer)) { + dispatch_source_cancel(_giftEffectTimer); + _giftEffectTimer = nil; + } + + // 清理队列 + if (_giftEffectsQueue) { + self.giftEffectsQueue = NULL; + } + + // 清理所有 banner 视图,防止 block 强引用 + [self cleanupAllSubviews]; + + // 清理 BannerScheduler + if (self.bannerScheduler) { + NSLog(@"�� 清理 BannerScheduler"); + [self.bannerScheduler clearQueue]; + [self.bannerScheduler pause]; + self.bannerScheduler = nil; + } + + // 清理手势识别器 + [self cleanupGestureRecognizers]; + + // 清理缓存管理器 + [self cleanupCacheManagers]; + + // 移除通知监听 + [self removeNotificationObservers]; + + NSLog(@"�� RoomAnimationView: 销毁完成"); +} + @end diff --git a/YuMi/Modules/YMRoom/View/XPRoomViewController.m b/YuMi/Modules/YMRoom/View/XPRoomViewController.m index f4c79371..2af546f7 100644 --- a/YuMi/Modules/YMRoom/View/XPRoomViewController.m +++ b/YuMi/Modules/YMRoom/View/XPRoomViewController.m @@ -604,7 +604,6 @@ XPCandyTreeInsufficientBalanceViewDelegate> self.view.backgroundColor = [UIColor darkGrayColor]; [self.view addSubview:self.backContainerView]; [self.view addSubview:self.littleGameView]; - [self.view addSubview:self.stageView]; [self.view addSubview:self.messageContainerView]; [self.view addSubview:self.quickMessageContainerView]; @@ -660,14 +659,8 @@ XPCandyTreeInsufficientBalanceViewDelegate> make.height.mas_equalTo(kNavigationHeight); }]; - [self.stageView mas_makeConstraints:^(MASConstraintMaker *make) { - make.leading.trailing.mas_equalTo(self.view); - make.top.mas_equalTo(self.roomHeaderView.mas_bottom); - make.height.mas_equalTo(self.stageView.hightForStageView); - }]; - [self.messageContainerView mas_makeConstraints:^(MASConstraintMaker *make) { - make.top.equalTo(self.stageView.mas_bottom); + make.top.equalTo(self.roomHeaderView.mas_bottom); make.bottom.equalTo(self.quickMessageContainerView.mas_top).offset(-5); make.leading.equalTo(self.view); make.trailing.equalTo(self.sideMenu.mas_leading).offset(-10); @@ -3195,9 +3188,16 @@ XPCandyTreeInsufficientBalanceViewDelegate> } - (StageView *)stageView { + // 🔧 修改:如果 StageViewManager 已初始化,返回其管理的 stageView + if (self.stageViewManager && self.stageViewManager.currentStageView) { + return self.stageViewManager.currentStageView; + } + + // 🔧 降级:只有在 StageViewManager 不可用时才创建默认的 SocialStageView if (!_stageView) { _stageView = [[SocialStageView alloc] initWithDelegate:self]; _stageView.alpha = 0; + NSLog(@"⚠️ 使用降级 stageView: SocialStageView"); } return _stageView; }