优化 RoomAnimationView 和 PIGiftBravoGiftBroadcastView 的内存管理,新增动画控制方法以提升用户体验。更新了连击视图的清理逻辑,确保资源的正确释放,避免内存泄漏。同时,新增 NetImageView 的取消加载功能,增强图片加载的灵活性和性能。重构相关方法以支持新的逻辑流程,提升代码可维护性和可读性。
This commit is contained in:
105
DOC/log.txt
105
DOC/log.txt
@@ -1,79 +1,26 @@
|
||||
-[XPSendGiftView xPGiftBarView:didClickSendGift:] [Line 771][Combo effect] 🎁 开始送礼物流程
|
||||
-[XPSendGiftView readyForCombo:gift:] [Line 727][Combo effect] 🔧 准备连击状态 - giftType: 18, segmentType: 8
|
||||
-[XPSendGiftView readyForCombo:gift:] [Line 745][Combo effect] ✅ 礼物支持连击,启用连击功能
|
||||
-[GiftComboManager activate] [Line 197][Combo effect] 🔧 激活连击功能
|
||||
-[GiftComboManager configureWithGiftInfo:targetUIDs:roomUID:sessionID:userInfo:countModel:sourceType:sendType:giftNum:] [Line 696][Combo effect] 🔧 统一配置连击参数
|
||||
-[GiftComboManager configureWithGiftInfo:targetUIDs:roomUID:sessionID:userInfo:countModel:sourceType:sendType:giftNum:] [Line 709][Combo effect] ✅ 连击参数配置完成 - giftId: 2263, targetCount: 1
|
||||
-[XPSendGiftView readyForCombo:gift:] [Line 761][Combo effect] ✅ 连击状态准备完成
|
||||
-[GiftComboManager printComboState] [Line 377][Combo effect] 📊 当前连击状态:
|
||||
-[GiftComboManager printComboState] [Line 378][Combo effect] - isCombing: NO
|
||||
-[GiftComboManager printComboState] [Line 379][Combo effect] - enableCombo: YES
|
||||
-[GiftComboManager printComboState] [Line 380][Combo effect] - combo: 1
|
||||
-[GiftComboManager printComboState] [Line 381][Combo effect] - hasGiftInfo: YES
|
||||
-[GiftComboManager printComboState] [Line 382][Combo effect] - targetCount: 1
|
||||
-[GiftComboManager printComboState] [Line 383][Combo effect] - errorMessage:
|
||||
-[XPSendGiftView xPGiftBarView:didClickSendGift:] [Line 778][Combo effect] ✅ 连击功能已启用,准备调用resetCombo
|
||||
-[XPSendGiftView sendGiftSuccess:originDic:uidCount:] [Line 1198][Combo effect] 📱 检查连击状态 - enableCombo: YES
|
||||
-[XPSendGiftView sendGiftSuccess:originDic:uidCount:] [Line 1207][Combo effect] 📱 originDic 连击计数检查 - comboCount: (null)
|
||||
-[XPSendGiftView sendGiftSuccess:originDic:uidCount:] [Line 1210][Combo effect] 📱 启用连击模式,重置连击状态
|
||||
-[GiftComboManager reset] [Line 141][Combo effect] 🔄 开始连击重置 - combo: 1 -> 1, enableCombo: YES, actionCallback: 为空
|
||||
-[GiftComboManager reset] [Line 154][Combo effect] 🔍 重置后验证 - combo: 1
|
||||
-[GiftComboManager reset] [Line 173][Combo effect] ⚠️ actionCallback为空,不显示连击面板
|
||||
-[GiftComboManager reset] [Line 177][Combo effect] 🎮 隐藏房间UI元素
|
||||
-[GiftComboManager reset] [Line 186][Combo effect] ✅ 连击重置完成 - isCombing: NO
|
||||
-[XPSendGiftView sendCustomMessage:oringinDic:] [Line 425][Combo effect] 📨 云信消息连击计数检查 - comboCount: 1, giftId: 2263
|
||||
-[GiftComboView setupTimer] [Line 284][Combo effect] ⏰ 设置连击倒计时
|
||||
-[CountdownRingView startCountdown] [Line 97][Combo effect] ⏰ 开始倒计时
|
||||
-[CountdownRingView animateRing] [Line 246][Combo effect] 🎬 环形动画已启动,时长: 5.0秒
|
||||
-[CountdownRingView startCountdown] [Line 109][Combo effect] ⏰ 倒计时已启动
|
||||
-[GiftComboView setupTimer] [Line 293][Combo effect] ⏰ 连击倒计时已启动
|
||||
-[GiftComboView updateCount] [Line 171][Combo effect] 🔢 更新连击次数显示 - combo: 1
|
||||
-[GiftComboManager printComboState] [Line 377][Combo effect] 📊 当前连击状态:
|
||||
-[GiftComboManager printComboState] [Line 378][Combo effect] - isCombing: NO
|
||||
-[GiftComboManager printComboState] [Line 379][Combo effect] - enableCombo: YES
|
||||
-[GiftComboManager printComboState] [Line 380][Combo effect] - combo: 1
|
||||
-[GiftComboManager printComboState] [Line 381][Combo effect] - hasGiftInfo: YES
|
||||
-[GiftComboManager printComboState] [Line 382][Combo effect] - targetCount: 1
|
||||
-[GiftComboManager printComboState] [Line 383][Combo effect] - errorMessage:
|
||||
-[GiftComboManager forceBoomStateReset] [Line 275][Combo effect] 🚨 执行强制Boom连击状态重置
|
||||
-[GiftComboManager forceBoomStateReset] [Line 278][Combo effect] ⏰ 停止所有定时器
|
||||
-[GiftComboManager forceStopAllTimers] [Line 321][Combo effect] ⏰ 强制停止所有Timer
|
||||
-[GiftComboManager forceBoomStateReset] [Line 282][Combo effect] 🗑️ 清空所有队列
|
||||
-[GiftComboManager forceBoomStateReset] [Line 287][Combo effect] 🔄 重置状态标志 - isCombing: NO -> NO
|
||||
-[GiftComboManager forceBoomStateReset] [Line 292][Combo effect] 🔄 combo计数重置为0
|
||||
-[GiftComboManager forceBoomStateReset] [Line 301][Combo effect] 📢 发送强制重置通知
|
||||
-[GiftComboManager forceBoomStateReset] [Line 309][Combo effect] 🎮 恢复房间UI元素
|
||||
-[GiftComboManager forceBoomStateReset] [Line 316][Combo effect] ✅ 强制重置完成 - enableCombo保持: YES, actionCallback保持: 为空
|
||||
-[XPSendGiftView removeAllComboRelatedViews] [Line 137][Combo effect] 🗑️ 开始移除连击相关视图
|
||||
-[XPSendGiftView removeAllComboRelatedViews] [Line 148][Combo effect] 🗑️ comboView存在但无superview,直接清理
|
||||
-[CountdownRingView stopCountdown] [Line 200][Combo effect] ⏰ 停止倒计时开始
|
||||
-[CountdownRingView stopCountdown] [Line 208][Combo effect] ⏰ Timer已停止
|
||||
-[CountdownRingView stopCountdown] [Line 214][Combo effect] ⏰ 动画已停止
|
||||
-[CountdownRingView stopCountdown] [Line 230][Combo effect] ⏰ 停止倒计时完成
|
||||
-[GiftComboView dealloc] [Line 43][Combo effect] 🗑️ GiftComboView dealloc开始 - 0x13624e600
|
||||
-[CountdownRingView stopCountdown] [Line 196][Combo effect] ⚠️ 倒计时未运行,无需停止
|
||||
-[GiftComboView dealloc] [Line 72][Combo effect] 🗑️ GiftComboView dealloc完成 - 0x13624e600
|
||||
-[XPSendGiftView removeAllComboRelatedViews] [Line 166][Combo effect] 🗑️ 连击相关视图移除完成
|
||||
-[CountdownRingView dealloc] [Line 32][Combo effect] 🗑️ CountdownRingView dealloc开始 - 0x13624e800
|
||||
-[CountdownRingView cleanup] [Line 251][Combo effect] 🗑️ 完全清理开始
|
||||
-[CountdownRingView stopCountdown] [Line 196][Combo effect] ⚠️ 倒计时未运行,无需停止
|
||||
-[CountdownRingView cleanup] [Line 260][Combo effect] 🗑️ 完全清理完成
|
||||
-[CountdownRingView dealloc] [Line 37][Combo effect] 🗑️ CountdownRingView dealloc完成 - 0x13624e800
|
||||
-[RoomAnimationView _handleGiftMessage:] [Line 1737][Combo effect] 📨 收到礼物消息 - second: 121
|
||||
-[RoomAnimationView _handleGiftMessage:] [Line 1757][Combo effect] 📨 礼物消息解析完成 - giftId: 2263, combo: 1, uid: 3184
|
||||
-[RoomAnimationView _handleGiftMessage:] [Line 1824][Combo effect] 🎁 处理普通礼物动画
|
||||
-[GiftAnimationManager enqueueGift:] [Line 237][Combo effect] 🎬 添加礼物到动画队列 - giftId: 2263, combo: 1
|
||||
-[GiftComboManager receiveGiftInfoForDisplayComboFlags:container:] [Line 407][Combo effect] 🎪 收到连击飘屏请求 - combo: 1, giftId: 2263
|
||||
-[GiftAnimationManager enqueueGift:]_block_invoke [Line 241][Combo effect] 📊 动画队列当前数量: 1
|
||||
-[GiftComboManager receiveGiftInfoForDisplayComboFlags:container:] [Line 410][Combo effect] 📊 连击飘屏队列数量: 1
|
||||
-[GiftComboManager processGiftFlagQueue] [Line 462][Combo effect] 🎪 处理连击飘屏 - combo: 1, giftId: 2263
|
||||
-[GiftComboManager processGiftFlagQueue] [Line 464][Combo effect] <20><> 移除后UI动画队列数量: 0
|
||||
-[GiftAnimationManager processNextGift]_block_invoke [Line 100][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 1
|
||||
-[GiftAnimationManager processNextGift]_block_invoke [Line 104][Combo effect] 📊 移除后动画队列数量: 0
|
||||
-[GiftAnimationManager distributeGiftAnimation:] [Line 119][Combo effect] 🎯 开始分发动画 - uid: 3184
|
||||
-[GiftAnimationManager distributeGiftAnimation:] [Line 122][Combo effect] 🎯 目标用户数量: 1
|
||||
-[GiftAnimationManager distributeGiftAnimation:] [Line 126][Combo effect] 🎯 是否使用连击动画: NO
|
||||
-[GiftAnimationManager distributeGiftAnimation:] [Line 133][Combo effect] 🎯 使用普通动画起点: {207, 285.79999999999995}
|
||||
-[GiftAnimationManager distributeGiftAnimation:] [Line 136][Combo effect] 🎯 动画延迟时间: 0.30
|
||||
-[GiftAnimationManager distributeGiftAnimation:] [Line 140][Combo effect] 🎯 为目标用户 3184 创建动画 - 终点: {207, 285.79999999999995}
|
||||
-[GiftAnimationManager processNextGift]_block_invoke [Line 82][Combo effect] 📭 动画队列为空,停止处理
|
||||
-[XPRoomViewController dealloc] [Line 314]🔄 XPRoomViewController: 清理 RoomAnimationView
|
||||
-[RoomAnimationView removeItSelf] [Line 193]<5D><> RoomAnimationView: 开始销毁
|
||||
-[RoomAnimationView cleanupAllSubviews] [Line 239] 清理所有子视图
|
||||
-[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: BravoGiftBannerView (从容器: XPRoomAnimationHitView)
|
||||
-[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: BravoGiftWinningFlagView (从容器: XPRoomAnimationHitView)
|
||||
-[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: BravoGiftWinningFlagView (从容器: XPRoomAnimationHitView)
|
||||
-[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: BravoGiftWinningFlagView (从容器: XPRoomAnimationHitView)
|
||||
-[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: BravoGiftWinningFlagView (从容器: XPRoomAnimationHitView)
|
||||
-[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: BravoGiftWinningFlagView (从容器: XPRoomAnimationHitView)
|
||||
-[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: NetImageView (从容器: XPRoomAnimationHitView)
|
||||
-[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: NetImageView (从容器: XPRoomAnimationHitView)
|
||||
-[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: NetImageView (从容器: XPRoomAnimationHitView)
|
||||
-[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: NetImageView (从容器: XPRoomAnimationHitView)
|
||||
-[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: NetImageView (从容器: XPRoomAnimationHitView)
|
||||
-[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: NetImageView (从容器: XPRoomAnimationHitView)
|
||||
-[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: NetImageView (从容器: XPRoomAnimationHitView)
|
||||
-[RoomAnimationView cleanupAllSubviews] [Line 265]🔄 所有子视图清理完成
|
||||
-[RoomAnimationView removeItSelf] [Line 220]<5D><> 清理 BannerScheduler
|
||||
-[RoomAnimationView cleanupGestureRecognizers] [Line 3690]🔄 清理手势识别器
|
||||
-[RoomAnimationView cleanupGestureRecognizers] [Line 3713]🔄 手势识别器清理完成
|
||||
-[RoomAnimationView cleanupCacheManagers] [Line 3717]🔄 清理缓存管理器
|
||||
-[RoomAnimationView cleanupCacheManagers] [Line 3730]🔄 缓存管理器清理完成
|
||||
-[RoomAnimationView removeNotificationObservers] [Line 3743]🔄 移除通知监听
|
||||
-[RoomAnimationView removeNotificationObservers] [Line 3753]🔄 通知监听移除完成
|
||||
-[RoomAnimationView removeItSelf] [Line 235]<5D><> RoomAnimationView: 销毁完成
|
||||
-[RoomAnimationView playBroveBanner:]_block_invoke [Line 752]🔄 BravoGiftBannerView complete 回调被调用
|
@@ -37,6 +37,9 @@ typedef NS_ENUM(NSInteger, NetImageState){
|
||||
- (void)loadImageWithUrl:(NSString * _Nonnull)imageUrl completion:(LoadCompletion _Nullable)completion fail:(LoadFail _Nullable)fail;
|
||||
|
||||
- (void)updateConfigPlaceHolder:(UIImage *)image;
|
||||
|
||||
- (void)cancelLoadImage;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@@ -43,6 +43,10 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)cancelLoadImage {
|
||||
[self sd_cancelCurrentImageLoad];
|
||||
}
|
||||
|
||||
- (void)initImageUrl:(NSString *)imageUrl {
|
||||
_imageUrl = imageUrl;
|
||||
_innerConfigUrl = [UIImageConstant configUrl:_imageUrl type:self.config.imageType radius:self.config.radius];
|
||||
|
@@ -186,50 +186,145 @@ BannerSchedulerDelegate
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
|
||||
NSLog(@" RoomAnimationView: dealloc 开始");
|
||||
[self removeItSelf];
|
||||
NSLog(@" RoomAnimationView: dealloc 完成");
|
||||
}
|
||||
|
||||
- (void)removeItSelf {
|
||||
NSLog(@"<22><> 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;
|
||||
}
|
||||
|
||||
// <EFBFBD><EFBFBD> 新增:清理所有 banner 视图,防止 block 强引用
|
||||
[self cleanupAllSubviews];
|
||||
|
||||
// 🔧 新增:清理 BannerScheduler
|
||||
if (self.bannerScheduler) {
|
||||
NSLog(@"<22><> 清理 BannerScheduler");
|
||||
[self.bannerScheduler clearQueue];
|
||||
[self.bannerScheduler pause];
|
||||
self.bannerScheduler = nil;
|
||||
}
|
||||
|
||||
// 清理手势识别器
|
||||
[self cleanupGestureRecognizers];
|
||||
|
||||
// 清理缓存管理器
|
||||
[self cleanupCacheManagers];
|
||||
|
||||
// 移除通知监听
|
||||
[self removeNotificationObservers];
|
||||
|
||||
NSLog(@"<22><> RoomAnimationView: 销毁完成");
|
||||
}
|
||||
|
||||
- (void)cancelAllAnimations {
|
||||
NSLog(@" 取消所有动画");
|
||||
|
||||
// 取消所有延迟执行
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self];
|
||||
|
||||
// 清理所有 POP 动画
|
||||
// 取消所有 POP 动画
|
||||
[self pop_removeAllAnimations];
|
||||
|
||||
// 清理定时器
|
||||
if (_giftEffectTimer && !dispatch_source_testcancel(_giftEffectTimer)) {
|
||||
dispatch_source_cancel(_giftEffectTimer);
|
||||
_giftEffectTimer = nil;
|
||||
// 取消所有子视图的 POP 动画
|
||||
NSArray *containers = @[self.bannerContainer, self.topContainer, self.middleContainer, self.bottomContainer];
|
||||
for (UIView *container in containers) {
|
||||
if (!container) continue;
|
||||
for (UIView *subview in container.subviews) {
|
||||
[subview pop_removeAllAnimations];
|
||||
}
|
||||
}
|
||||
|
||||
// 清理队列
|
||||
if (_giftEffectsQueue) {
|
||||
self.giftEffectsQueue = NULL;
|
||||
// 取消所有 UIView 动画
|
||||
[UIView setAnimationsEnabled:NO];
|
||||
|
||||
NSLog(@"🔄 所有动画取消完成");
|
||||
}
|
||||
|
||||
- (void)cleanupAllSubviews {
|
||||
NSLog(@" 清理所有子视图");
|
||||
|
||||
// 清理所有容器的子视图
|
||||
NSArray *containers = @[self.bannerContainer, self.topContainer, self.middleContainer, self.bottomContainer];
|
||||
|
||||
for (UIView *container in containers) {
|
||||
if (!container) continue;
|
||||
|
||||
NSMutableArray *viewsToRemove = [NSMutableArray array];
|
||||
for (UIView *subview in container.subviews) {
|
||||
[viewsToRemove addObject:subview];
|
||||
NSLog(@" 标记移除视图: %@ (从容器: %@)", NSStringFromClass([subview class]), NSStringFromClass([container class]));
|
||||
}
|
||||
|
||||
for (UIView *view in viewsToRemove) {
|
||||
// 取消视图上的所有动画
|
||||
[view pop_removeAllAnimations];
|
||||
|
||||
// 清理视图上的手势识别器
|
||||
if (view.gestureRecognizers.count > 0) {
|
||||
NSLog(@" 清理视图 %@ 上的 %lu 个手势识别器", NSStringFromClass([view class]), (unsigned long)view.gestureRecognizers.count);
|
||||
for (UIGestureRecognizer *gesture in view.gestureRecognizers.copy) {
|
||||
[view removeGestureRecognizer:gesture];
|
||||
}
|
||||
}
|
||||
|
||||
// 强制移除视图
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
}
|
||||
|
||||
// 🔧 新增:清理 BannerScheduler
|
||||
if (self.bannerScheduler) {
|
||||
NSLog(@"<22><> 清理 BannerScheduler");
|
||||
[self.bannerScheduler clearQueue];
|
||||
[self.bannerScheduler pause];
|
||||
self.bannerScheduler = nil;
|
||||
NSLog(@"🔄 所有子视图清理完成");
|
||||
}
|
||||
|
||||
- (void)__cleanupAllBannerViews {
|
||||
NSLog(@"<22><> 清理所有 Banner 视图");
|
||||
|
||||
// 清理 bannerContainer 中的所有子视图
|
||||
NSMutableArray *viewsToRemove = [NSMutableArray array];
|
||||
for (UIView *subview in self.bannerContainer.subviews) {
|
||||
[viewsToRemove addObject:subview];
|
||||
NSLog(@"🔄 标记移除 banner: %@", NSStringFromClass([subview class]));
|
||||
}
|
||||
|
||||
// 清理手势识别器
|
||||
[self cleanupGestureRecognizers];
|
||||
for (UIView *view in viewsToRemove) {
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
|
||||
// 清理缓存管理器
|
||||
[self cleanupCacheManagers];
|
||||
// 清理 topContainer 中的所有子视图(可能包含 CP 相关的 banner)
|
||||
viewsToRemove = [NSMutableArray array];
|
||||
for (UIView *subview in self.topContainer.subviews) {
|
||||
if ([subview isKindOfClass:[CPBindingAnimation class]] ||
|
||||
[subview isKindOfClass:[CPLevelUpAnimation class]]) {
|
||||
[viewsToRemove addObject:subview];
|
||||
NSLog(@"<22><> 标记移除 CP banner: %@", NSStringFromClass([subview class]));
|
||||
}
|
||||
}
|
||||
|
||||
// 移除通知监听
|
||||
[self removeNotificationObservers];
|
||||
for (UIView *view in viewsToRemove) {
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
|
||||
NSLog(@"<EFBFBD><EFBFBD> RoomAnimationView: 销毁完成");
|
||||
NSLog(@"🔄 Banner 视图清理完成");
|
||||
}
|
||||
|
||||
-(void)resumeTimer{
|
||||
@@ -541,7 +636,7 @@ BannerSchedulerDelegate
|
||||
return;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
#if 0
|
||||
// DEBUG环境:复制banner数据用于测试
|
||||
BOOL enableBannerCopy = obj.second == Custom_Message_Sub_Super_Gift_Banner;// YES; // 设置为NO可以禁用复制功能
|
||||
NSInteger copyCount = 10; // 可以调整这个数字来改变复制数量
|
||||
@@ -669,25 +764,40 @@ BannerSchedulerDelegate
|
||||
|
||||
- (void)playBroveBanner:(AttachmentModel *)obj {
|
||||
if (!obj.data) {
|
||||
self.isRoomBannerV2Displaying = NO;
|
||||
[self.bannerScheduler markBannerFinished];
|
||||
return;
|
||||
}
|
||||
self.isRoomBannerV2Displaying = YES;
|
||||
@kWeakify(self);
|
||||
RoomInfoModel *roomInfo = self.hostDelegate.getRoomInfo;
|
||||
[BravoGiftBannerView display:self.bannerContainer
|
||||
inRoomUid:roomInfo.uid
|
||||
with:obj
|
||||
complete:^{
|
||||
@kStrongify(self);
|
||||
NSLog(@"🔄 BravoGiftBannerView complete 回调被调用");
|
||||
self.isRoomBannerV2Displaying = NO;
|
||||
[self.bannerScheduler markBannerFinished];
|
||||
} exitCurrentRoom:^{
|
||||
@kStrongify(self);
|
||||
[self.hostDelegate exitRoom];
|
||||
}];
|
||||
self.isRoomBannerV2Displaying = NO;
|
||||
[self.bannerScheduler markBannerFinished];
|
||||
return;
|
||||
}
|
||||
self.isRoomBannerV2Displaying = YES;
|
||||
@kWeakify(self);
|
||||
RoomInfoModel *roomInfo = self.hostDelegate.getRoomInfo;
|
||||
|
||||
// <EFBFBD><EFBFBD> 新增:检查是否正在销毁
|
||||
if (!self || !self.superview) {
|
||||
NSLog(@"⚠️ RoomAnimationView 正在销毁,跳过 banner 播放");
|
||||
return;
|
||||
}
|
||||
|
||||
[BravoGiftBannerView display:self.bannerContainer
|
||||
inRoomUid:roomInfo.uid
|
||||
with:obj
|
||||
complete:^{
|
||||
@kStrongify(self);
|
||||
if (!self || !self.superview) {
|
||||
NSLog(@"⚠️ RoomAnimationView 已被释放,跳过 complete 回调");
|
||||
return;
|
||||
}
|
||||
NSLog(@"🔄 BravoGiftBannerView complete 回调被调用");
|
||||
self.isRoomBannerV2Displaying = NO;
|
||||
[self.bannerScheduler markBannerFinished];
|
||||
} exitCurrentRoom:^{
|
||||
@kStrongify(self);
|
||||
if (!self || !self.superview) {
|
||||
NSLog(@"⚠️ RoomAnimationView 已被释放,跳过 exitCurrentRoom 回调");
|
||||
return;
|
||||
}
|
||||
[self.hostDelegate exitRoom];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)playLuckyPackageBanner:(AttachmentModel *)obj {
|
||||
|
@@ -11,6 +11,13 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface PIGiftBravoGiftBroadcastView : UIView
|
||||
|
||||
// 开始动画循环
|
||||
- (void)startAnimation;
|
||||
|
||||
// 停止动画循环
|
||||
- (void)stopAnimation;
|
||||
|
||||
// 兼容旧方法
|
||||
- (void)endloop;
|
||||
|
||||
@end
|
||||
|
@@ -8,24 +8,123 @@
|
||||
#import "PIGiftBravoGiftBroadcastView.h"
|
||||
#import "XPSkillCardPlayerManager.h"
|
||||
#import "BravoGiftTabInfomationModel.h"
|
||||
|
||||
|
||||
@interface PIGiftBravoGiftBroadcastItemView : UIView
|
||||
|
||||
@property (nonatomic, strong) BravoGiftTabInfomationModel *model;
|
||||
- (void)cancelLoading;
|
||||
|
||||
@end
|
||||
|
||||
@implementation PIGiftBravoGiftBroadcastItemView
|
||||
{
|
||||
NetImageView *avatar;
|
||||
UILabel *content;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
NetImageConfig *config = [[NetImageConfig alloc] init];
|
||||
config.placeHolder = [UIImageConstant defaultAvatarPlaceholder];
|
||||
config.imageType = ImageTypeUserIcon;
|
||||
avatar = [[NetImageView alloc] initWithConfig:config];
|
||||
|
||||
content = [[UILabel alloc] init];
|
||||
|
||||
[self addSubview:avatar];
|
||||
[self addSubview:content];
|
||||
|
||||
[avatar mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerY.mas_equalTo(self);
|
||||
make.leading.mas_equalTo(8);
|
||||
make.size.mas_equalTo(CGSizeMake(24, 24));
|
||||
}];
|
||||
|
||||
[content mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerY.mas_equalTo(self);
|
||||
make.leading.mas_equalTo(avatar.mas_trailing).offset(4);
|
||||
make.trailing.mas_equalTo(self).offset(-4);
|
||||
}];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setModel:(BravoGiftTabInfomationModel *)model {
|
||||
_model = model;
|
||||
|
||||
avatar.imageUrl = model.avatar;
|
||||
|
||||
NSTextAttachment *coinAttachment = [[NSTextAttachment alloc] init];
|
||||
coinAttachment.image = kImage(@"moli_money_icon");
|
||||
coinAttachment.bounds = CGRectMake(0, -3, 18, 18);
|
||||
NSAttributedString *coinString = [NSAttributedString attributedStringWithAttachment:coinAttachment];
|
||||
NSString *giftName = model.giftName;
|
||||
NSString *coin = @(model.coin).stringValue;
|
||||
NSString *oringinalString = [NSString stringWithFormat:@" %@ %@ %@ %@ %@ ",
|
||||
[NSString trimString:model.nick lengthLimit:4],
|
||||
YMLocalizedString(@"Combo_7"),
|
||||
giftName, YMLocalizedString(@"Combo_4"), coin];
|
||||
|
||||
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] init];
|
||||
[string appendAttributedString:[[NSAttributedString alloc] initWithString:oringinalString attributes:@{
|
||||
NSFontAttributeName: kFontRegular(13),
|
||||
NSForegroundColorAttributeName: [UIColor whiteColor]
|
||||
}]];
|
||||
|
||||
// 找到 giftName 和 coin 在字符串中的位置
|
||||
NSRange giftNameRange = [oringinalString rangeOfString:giftName];
|
||||
NSRange coinRange = [oringinalString rangeOfString:coin];
|
||||
|
||||
// 创建黄色文本的属性
|
||||
NSDictionary *yellowAttributes_name = @{
|
||||
NSFontAttributeName: kFontRegular(13),
|
||||
NSForegroundColorAttributeName: UIColorFromRGB(0xffec6f)
|
||||
};
|
||||
NSDictionary *yellowAttributes_coin = @{
|
||||
NSFontAttributeName: kFontSemibold(16),
|
||||
NSForegroundColorAttributeName: UIColorFromRGB(0xffec6f)
|
||||
};
|
||||
// 创建包含黄色文本的可变字符串
|
||||
NSMutableAttributedString *yellowGiftName = [[NSMutableAttributedString alloc] initWithString:giftName attributes:yellowAttributes_name];
|
||||
NSMutableAttributedString *yellowCoin = [[NSMutableAttributedString alloc] initWithString:coin attributes:yellowAttributes_coin];
|
||||
|
||||
// 替换原始字符串中的 giftName 和 coin 为黄色文本
|
||||
[string replaceCharactersInRange:giftNameRange withAttributedString:yellowGiftName];
|
||||
[string replaceCharactersInRange:coinRange withAttributedString:yellowCoin];
|
||||
|
||||
[string appendAttributedString:coinString];
|
||||
|
||||
[content setAttributedText:string.copy];
|
||||
}
|
||||
|
||||
- (void)cancelLoading {
|
||||
[avatar cancelLoadImage];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface PIGiftBravoGiftBroadcastView()
|
||||
|
||||
@property (nonatomic, strong) UIImageView *speaker;
|
||||
@property (nonatomic, strong) MarqueeLabel *label;
|
||||
@property (nonatomic, strong) UIView *container;
|
||||
@property (nonatomic, copy) NSArray <BravoGiftTabInfomationModel *>* source;
|
||||
@property (nonatomic, strong) NSMutableArray *labels;
|
||||
@property (nonatomic, strong) NSMutableArray<PIGiftBravoGiftBroadcastItemView *> *labels;
|
||||
@property (nonatomic, assign) NSInteger index;
|
||||
@property (nonatomic, assign) NSInteger loopStatus; // -1: 頁面回收,0:未啟動,1:啟動中
|
||||
@property (nonatomic, strong) NSMutableArray *imageLoaders;
|
||||
@property (nonatomic, assign) BOOL isAnimating; // 动画状态控制
|
||||
@property (nonatomic, assign) BOOL shouldStopAnimation; // 停止动画标志
|
||||
@property (nonatomic, strong) UIButton *tipsButton;
|
||||
@property (nonatomic, strong) NSTimer *animationTimer; // 动画定时器
|
||||
|
||||
@end
|
||||
|
||||
@implementation PIGiftBravoGiftBroadcastView
|
||||
|
||||
- (void)dealloc {
|
||||
|
||||
[self endloop];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
@@ -59,124 +158,142 @@
|
||||
}];
|
||||
|
||||
self.labels = [[NSMutableArray alloc] init];
|
||||
self.imageLoaders = [[NSMutableArray alloc] init];
|
||||
self.isAnimating = NO;
|
||||
self.shouldStopAnimation = NO;
|
||||
|
||||
@kWeakify(self);
|
||||
[self.source enumerateObjectsUsingBlock:^(BravoGiftTabInfomationModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
@kStrongify(self);
|
||||
NetImageView *avatar = [[NetImageView alloc] init];
|
||||
[avatar loadImageWithUrl:obj.avatar completion:^(UIImage * _Nullable image, NSURL * _Nonnull url) {
|
||||
NSTextAttachment *avatarAttachment = [[NSTextAttachment alloc] init];
|
||||
avatarAttachment.image = [image roundedImageWithCornerRadius:12*3 size:CGSizeMake(24*3, 24*3)];
|
||||
avatarAttachment.bounds = CGRectMake(0, -5, 24, 24);
|
||||
NSTextAttachment *coinAttachment = [[NSTextAttachment alloc] init];
|
||||
coinAttachment.image = kImage(@"moli_money_icon");
|
||||
coinAttachment.bounds = CGRectMake(0, -3, 18, 18);
|
||||
NSAttributedString *avatarString = [NSAttributedString attributedStringWithAttachment:avatarAttachment];
|
||||
NSAttributedString *coinString = [NSAttributedString attributedStringWithAttachment:coinAttachment];
|
||||
NSString *giftName = obj.giftName;
|
||||
NSString *coin = @(obj.coin).stringValue;
|
||||
NSString *oringinalString = [NSString stringWithFormat:@" %@ %@ %@ %@ %@ ",
|
||||
[NSString trimString:obj.nick lengthLimit:4],
|
||||
YMLocalizedString(@"Combo_7"),
|
||||
giftName, YMLocalizedString(@"Combo_4"), coin];
|
||||
|
||||
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] init];
|
||||
[string appendAttributedString:[[NSAttributedString alloc] initWithString:oringinalString attributes:@{
|
||||
NSFontAttributeName: kFontRegular(13),
|
||||
NSForegroundColorAttributeName: [UIColor whiteColor]
|
||||
}]];
|
||||
|
||||
// 找到 giftName 和 coin 在字符串中的位置
|
||||
NSRange giftNameRange = [oringinalString rangeOfString:giftName];
|
||||
NSRange coinRange = [oringinalString rangeOfString:coin];
|
||||
|
||||
// 创建黄色文本的属性
|
||||
NSDictionary *yellowAttributes_name = @{
|
||||
NSFontAttributeName: kFontRegular(13),
|
||||
NSForegroundColorAttributeName: UIColorFromRGB(0xffec6f)
|
||||
};
|
||||
NSDictionary *yellowAttributes_coin = @{
|
||||
NSFontAttributeName: kFontSemibold(16),
|
||||
NSForegroundColorAttributeName: UIColorFromRGB(0xffec6f)
|
||||
};
|
||||
// 创建包含黄色文本的可变字符串
|
||||
NSMutableAttributedString *yellowGiftName = [[NSMutableAttributedString alloc] initWithString:giftName attributes:yellowAttributes_name];
|
||||
NSMutableAttributedString *yellowCoin = [[NSMutableAttributedString alloc] initWithString:coin attributes:yellowAttributes_coin];
|
||||
|
||||
// 替换原始字符串中的 giftName 和 coin 为黄色文本
|
||||
[string replaceCharactersInRange:giftNameRange withAttributedString:yellowGiftName];
|
||||
[string replaceCharactersInRange:coinRange withAttributedString:yellowCoin];
|
||||
|
||||
[string insertAttributedString:avatarString atIndex:0];
|
||||
[string appendAttributedString:coinString];
|
||||
|
||||
UILabel *label = [[UILabel alloc] init];
|
||||
label.attributedText = string.copy;
|
||||
[self.labels addObject:label];
|
||||
if (self.loopStatus>-1) {
|
||||
[self startLoop];
|
||||
}
|
||||
}];
|
||||
[self.imageLoaders addObject:avatar];
|
||||
PIGiftBravoGiftBroadcastItemView *item = [[PIGiftBravoGiftBroadcastItemView alloc] init];
|
||||
item.frame = CGRectMake(0, 0, KScreenWidth - 25 - 42, 26);
|
||||
item.model = obj;
|
||||
[self.labels addObject:item];
|
||||
}];
|
||||
|
||||
// 开始动画
|
||||
[self startLoop];
|
||||
}
|
||||
|
||||
- (void)startLoop {
|
||||
if (self.loopStatus > 0 || self.labels.count == 0) {
|
||||
if (self.isAnimating || self.labels.count == 0 || self.shouldStopAnimation) {
|
||||
return;
|
||||
}
|
||||
self.loopStatus = 1;
|
||||
|
||||
// 获取当前索引的标签
|
||||
UILabel *label = [self.labels xpSafeObjectAtIndex:self.index];
|
||||
if (label) {
|
||||
// 设置标签的初始位置(屏幕右侧)
|
||||
label.frame = CGRectMake(KScreenWidth, 2, KScreenWidth - 25 - 42, 26);
|
||||
// 将标签添加到容器视图
|
||||
[self.container addSubview:label];
|
||||
|
||||
// 动画:标签从屏幕右侧移动到左侧
|
||||
[UIView animateWithDuration:0.5 animations:^{
|
||||
label.frame = CGRectMake(25, 2, KScreenWidth - 25 - 42, 26);
|
||||
} completion:^(BOOL finished) {
|
||||
// 延迟动画:标签从左侧移出屏幕
|
||||
[UIView animateWithDuration:0.5 delay:2 options:UIViewAnimationOptionCurveEaseIn animations:^{
|
||||
label.frame = CGRectMake(-KScreenWidth, 2, KScreenWidth - 25 - 42, 26);
|
||||
} completion:^(BOOL finished) {
|
||||
// 移除标签
|
||||
[label removeFromSuperview];
|
||||
// 索引递增
|
||||
self.index += 1;
|
||||
|
||||
// 检查是否到达最后一个标签
|
||||
if (self.index >= self.labels.count) {
|
||||
// 重置索引为0
|
||||
self.index = 0;
|
||||
}
|
||||
|
||||
// 设置循环标志为NO
|
||||
self.loopStatus = 0;
|
||||
// 递归调用startLoop继续循环
|
||||
[self startLoop];
|
||||
}];
|
||||
}];
|
||||
self.isAnimating = YES;
|
||||
[self playCurrentAnimation];
|
||||
}
|
||||
|
||||
// 公共方法:开始动画循环
|
||||
- (void)startAnimation {
|
||||
if (self.shouldStopAnimation) {
|
||||
self.shouldStopAnimation = NO;
|
||||
}
|
||||
|
||||
if (self.labels.count > 0) {
|
||||
[self startLoop];
|
||||
}
|
||||
}
|
||||
|
||||
// 公共方法:停止动画循环
|
||||
- (void)stopAnimation {
|
||||
[self endloop];
|
||||
}
|
||||
|
||||
- (void)playCurrentAnimation {
|
||||
if (self.shouldStopAnimation) {
|
||||
self.isAnimating = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取当前索引的视图
|
||||
PIGiftBravoGiftBroadcastItemView *item = [self.labels xpSafeObjectAtIndex:self.index];
|
||||
if (!item) {
|
||||
self.isAnimating = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置初始位置(屏幕右侧)
|
||||
item.frame = CGRectMake(KScreenWidth, 2, KScreenWidth - 25 - 42, 26);
|
||||
// 添加到容器视图
|
||||
[self.container addSubview:item];
|
||||
|
||||
@kWeakify(self);
|
||||
// 入场动画:0.5秒
|
||||
[UIView animateWithDuration:0.5 animations:^{
|
||||
item.frame = CGRectMake(25, 2, KScreenWidth - 25 - 42, 26);
|
||||
} completion:^(BOOL finished) {
|
||||
@kStrongify(self);
|
||||
if (!finished || self.shouldStopAnimation) {
|
||||
[item removeFromSuperview];
|
||||
self.isAnimating = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
// 停留时间:2秒延迟后执行出场动画
|
||||
[UIView animateWithDuration:0.5 delay:2.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
|
||||
item.frame = CGRectMake(-KScreenWidth, 2, KScreenWidth - 25 - 42, 26);
|
||||
} completion:^(BOOL finished) {
|
||||
@kStrongify(self);
|
||||
// 移除视图
|
||||
[item removeFromSuperview];
|
||||
|
||||
if (self.shouldStopAnimation) {
|
||||
self.isAnimating = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
// 索引递增
|
||||
self.index += 1;
|
||||
if (self.index >= self.labels.count) {
|
||||
self.index = 0;
|
||||
}
|
||||
|
||||
// 使用定时器延迟下一个动画,避免递归调用
|
||||
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(scheduleNextAnimation) userInfo:nil repeats:NO];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)scheduleNextAnimation {
|
||||
[self.animationTimer invalidate];
|
||||
self.animationTimer = nil;
|
||||
|
||||
if (self.shouldStopAnimation) {
|
||||
self.isAnimating = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
[self playCurrentAnimation];
|
||||
}
|
||||
|
||||
- (void)endloop {
|
||||
self.loopStatus = -1;
|
||||
// 设置停止标志
|
||||
self.shouldStopAnimation = YES;
|
||||
self.isAnimating = NO;
|
||||
|
||||
// 停止并清理定时器
|
||||
if (self.animationTimer) {
|
||||
[self.animationTimer invalidate];
|
||||
self.animationTimer = nil;
|
||||
}
|
||||
|
||||
// 取消所有动画
|
||||
[self.container.layer removeAllAnimations];
|
||||
|
||||
// 清理图片加载器
|
||||
[self.imageLoaders removeAllObjects];
|
||||
// 取消每个条目的图片加载
|
||||
for (PIGiftBravoGiftBroadcastItemView *item in self.labels) {
|
||||
[item cancelLoading];
|
||||
}
|
||||
|
||||
// 清理标签
|
||||
for (UILabel *label in self.labels) {
|
||||
[label removeFromSuperview];
|
||||
// 清理视图
|
||||
for (PIGiftBravoGiftBroadcastItemView *item in self.labels) {
|
||||
[item.layer removeAllAnimations];
|
||||
[item removeFromSuperview];
|
||||
}
|
||||
[self.labels removeAllObjects];
|
||||
|
||||
// 重置状态
|
||||
self.index = 0;
|
||||
}
|
||||
|
||||
- (void)didTapTips {
|
||||
|
@@ -124,12 +124,10 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
|
||||
[[GiftComboManager sharedManager] registerActions:nil];
|
||||
|
||||
if (_bravoGiftView) {
|
||||
[self.bravoGiftView endloop];
|
||||
[_bravoGiftView endloop];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma mark - 连击状态管理
|
||||
|
||||
// 移除连击相关视图
|
||||
|
@@ -95,7 +95,7 @@
|
||||
[regularList enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
@kStrongify(self);
|
||||
NIMChatroomMember * member = obj;
|
||||
if (member.type == NIMTeamMemberTypeOwner) {
|
||||
if (member.type == NIMChatroomMemberTypeCreator) {
|
||||
[self.datasource insertObject:member atIndex:0];
|
||||
}else {
|
||||
[self.datasource addObject:member];
|
||||
@@ -129,7 +129,7 @@
|
||||
NSArray * regularList = x;
|
||||
[regularList enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
NIMChatroomMember * member = obj;
|
||||
if (member.type == NIMTeamMemberTypeOwner) {
|
||||
if (member.type == NIMChatroomMemberTypeCreator) {
|
||||
[self.datasource insertObject:member atIndex:0];
|
||||
}else {
|
||||
[self.datasource addObject:member];
|
||||
@@ -326,6 +326,9 @@
|
||||
}
|
||||
|
||||
- (void)handleUpMicAction:(NIMChatroomMember *)member {
|
||||
|
||||
// TODO : 发送类似 31 的消息
|
||||
|
||||
if ([AccountInfoStorage instance].getUid.integerValue == member.userId.integerValue) {
|
||||
[Api getUserInfo:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
|
||||
if (code == 200) {
|
||||
|
Reference in New Issue
Block a user