优化 PIGiftBravoGiftBroadcastView 动画逻辑,调整动画时序和状态管理,增加数据源检查,确保动画流畅性和稳定性。同时,添加详细日志输出以便于调试和监控。
This commit is contained in:
@@ -1 +1 @@
|
||||
57817
|
||||
56756
|
@@ -376,24 +376,27 @@
|
||||
if (message.session.sessionType == NIMSessionTypeChatroom) {
|
||||
NSString *sessionId = message.session.sessionId;
|
||||
if ([sessionId isEqualToString:self.currentPublicRoomId]) {
|
||||
NIMMessageChatroomExtension *messageExt = (NIMMessageChatroomExtension *)message.messageExt;
|
||||
|
||||
AttachmentModel *attachment;
|
||||
if (message.messageType == NIMMessageTypeCustom) {
|
||||
NIMCustomObject *obj = (NIMCustomObject *) message.messageObject;
|
||||
attachment = (AttachmentModel *) obj.attachment;
|
||||
if (attachment) {
|
||||
switch (attachment.first) {
|
||||
case CustomMessageType_Super_Gift:
|
||||
[self handleFirst_106:attachment
|
||||
message:message];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (attachment.first > 0 && attachment.second >0) {
|
||||
[self handleMessageWithAttachmentAndFirstSecond:message];
|
||||
}
|
||||
// if (attachment) {
|
||||
// switch (attachment.first) {
|
||||
// case CustomMessageType_Super_Gift:
|
||||
// [self handleFirst_106:attachment
|
||||
// message:message];
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// NIMMessageChatroomExtension *messageExt = (NIMMessageChatroomExtension *)message.messageExt;
|
||||
// NSLog(@"PublicRoomManager: 收到公共房间消息: %@\n%@",
|
||||
// message.rawAttachContent,
|
||||
// messageExt.roomExt);
|
||||
@@ -402,8 +405,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleMessageWithAttachmentAndFirstSecond:(NIMMessage *)message {
|
||||
|
||||
}
|
||||
|
||||
- (void)handleFirst_106:(AttachmentModel *)attachment
|
||||
message:(NIMMessage *)message {
|
||||
|
||||
// allRoomMsg
|
||||
|
||||
// 只有用户在房间时,才会转发
|
||||
if (![XPSkillCardPlayerManager shareInstance].isInRoom) {
|
||||
NSLog(@"PublicRoomManager: 用户未在房间中,跳过消息转发");
|
||||
|
@@ -799,6 +799,9 @@ BannerSchedulerDelegate
|
||||
NSLog(@"🔄 BravoGiftBannerView complete 回调被调用");
|
||||
self.isRoomBannerV2Displaying = NO;
|
||||
[self.bannerScheduler markBannerFinished];
|
||||
|
||||
// 🔧 新增:确保手势容器状态正确
|
||||
[self ensureBannerGestureContainersEnabled];
|
||||
} exitCurrentRoom:^{
|
||||
@kStrongify(self);
|
||||
if (!self || !self.superview) {
|
||||
@@ -826,6 +829,9 @@ BannerSchedulerDelegate
|
||||
NSLog(@"🔄 LuckyPackageBannerView complete 回调被调用");
|
||||
self.isRoomBannerV2Displaying = NO;
|
||||
[self.bannerScheduler markBannerFinished];
|
||||
|
||||
// 🔧 新增:确保手势容器状态正确
|
||||
[self ensureBannerGestureContainersEnabled];
|
||||
} exitCurrentRoom:^{
|
||||
@kStrongify(self);
|
||||
[self.hostDelegate exitRoom];
|
||||
@@ -851,6 +857,9 @@ BannerSchedulerDelegate
|
||||
NSLog(@"🔄 RoomHighValueGiftBannerAnimation complete 回调被调用");
|
||||
self.isRoomBannerV2Displaying = NO;
|
||||
[self.bannerScheduler markBannerFinished];
|
||||
|
||||
// 🔧 新增:确保手势容器状态正确
|
||||
[self ensureBannerGestureContainersEnabled];
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -866,6 +875,9 @@ BannerSchedulerDelegate
|
||||
@kStrongify(self);
|
||||
self.isRoomBannerV2Displaying = NO;
|
||||
[self.bannerScheduler markBannerFinished];
|
||||
|
||||
// 🔧 新增:确保手势容器状态正确
|
||||
[self ensureBannerGestureContainersEnabled];
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -878,6 +890,9 @@ BannerSchedulerDelegate
|
||||
NSLog(@"🔄 CPGiftBanner complete 回调被调用");
|
||||
self.isRoomBannerV2Displaying = NO;
|
||||
[self.bannerScheduler markBannerFinished];
|
||||
|
||||
// 🔧 新增:确保手势容器状态正确
|
||||
[self ensureBannerGestureContainersEnabled];
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -889,6 +904,9 @@ BannerSchedulerDelegate
|
||||
@kStrongify(self);
|
||||
self.isRoomBannerV2Displaying = NO;
|
||||
[self.bannerScheduler markBannerFinished];
|
||||
|
||||
// 🔧 新增:确保手势容器状态正确
|
||||
[self ensureBannerGestureContainersEnabled];
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -1052,6 +1070,9 @@ BannerSchedulerDelegate
|
||||
NSLog(@"🔄 LuckyGiftWinningBannerView complete 回调被调用");
|
||||
self.isRoomBannerV2Displaying = NO;
|
||||
[self.bannerScheduler markBannerFinished];
|
||||
|
||||
// 🔧 新增:确保手势容器状态正确
|
||||
[self ensureBannerGestureContainersEnabled];
|
||||
} exitCurrentRoom:^{
|
||||
@kStrongify(self);
|
||||
[self.hostDelegate exitRoom];
|
||||
@@ -1091,6 +1112,9 @@ BannerSchedulerDelegate
|
||||
NSLog(@"🔄 GameUniversalBannerView complete 回调被调用");
|
||||
self.isRoomBannerV2Displaying = NO;
|
||||
[self.bannerScheduler markBannerFinished];
|
||||
|
||||
// 🔧 新增:确保手势容器状态正确
|
||||
[self ensureBannerGestureContainersEnabled];
|
||||
} goToGame:^(NSInteger gameID) {
|
||||
@kStrongify(self);
|
||||
NSArray *baishunList = [self.hostDelegate getPlayList];
|
||||
@@ -2426,7 +2450,28 @@ BannerSchedulerDelegate
|
||||
- (void)handleBannerTap:(UITapGestureRecognizer *)tapGesture {
|
||||
CGPoint tapPoint = [tapGesture locationInView:self.bannerContainer];
|
||||
|
||||
// 检查当前显示的 banner 是否在 tap 位置可以响应事件
|
||||
// 🔧 新增:检查是否有可见的 banner
|
||||
BOOL hasVisibleBanner = NO;
|
||||
for (UIView *subview in self.bannerContainer.subviews) {
|
||||
if (!subview.hidden && subview.alpha > 0.01) {
|
||||
hasVisibleBanner = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有可见的 banner,直接转发点击事件到下层
|
||||
if (!hasVisibleBanner) {
|
||||
NSLog(@"🎯 没有可见 banner,直接转发点击事件到下层");
|
||||
self.savedTapPoint = tapPoint;
|
||||
self.hasSavedTapPoint = YES;
|
||||
CGPoint screenPoint = [self.bannerContainer convertPoint:tapPoint toView:nil];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"BannerTapToFunctionContainer"
|
||||
object:nil
|
||||
userInfo:@{@"point": [NSValue valueWithCGPoint:screenPoint]}];
|
||||
return;
|
||||
}
|
||||
|
||||
// 有可见 banner 时的原有逻辑
|
||||
if ([self isPointInBannerInteractiveArea:tapPoint]) {
|
||||
// banner 可以响应,不处理,让 banner 继续原有逻辑
|
||||
NSLog(@"🎯 Banner tap 位置在可交互区域,banner 将处理此事件");
|
||||
@@ -2440,7 +2485,6 @@ BannerSchedulerDelegate
|
||||
NSLog(@"💾 Banner tap 位置不在可交互区域,已保存位置: %@", NSStringFromCGPoint(tapPoint));
|
||||
// 将 bannerContainer 中的点转换为屏幕坐标系
|
||||
CGPoint screenPoint = [self.bannerContainer convertPoint:tapPoint toView:nil];
|
||||
// UIView *tappedView = tapGesture.view;
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"BannerTapToFunctionContainer"
|
||||
object:nil
|
||||
userInfo:@{@"point": [NSValue valueWithCGPoint:screenPoint]}];
|
||||
@@ -3786,6 +3830,9 @@ BannerSchedulerDelegate
|
||||
- (void)bannerSchedulerDidFinishPlaying:(BannerScheduler *)scheduler {
|
||||
// Banner 播放完成,可以在这里进行清理工作
|
||||
NSLog(@"🔄 BannerScheduler: Banner 播放完成");
|
||||
|
||||
// 🔧 新增:确保手势容器状态正确
|
||||
[self ensureBannerGestureContainersEnabled];
|
||||
}
|
||||
|
||||
- (void)bannerScheduler:(BannerScheduler *)scheduler didStartPlayingBanner:(id)banner {
|
||||
@@ -3904,6 +3951,29 @@ BannerSchedulerDelegate
|
||||
NSLog(@"🎯 RoomAnimationView: Banner 手势容器已恢复显示(非小游戏模式)");
|
||||
}
|
||||
|
||||
// 🔧 新增:确保手势容器状态正确
|
||||
- (void)ensureBannerGestureContainersEnabled {
|
||||
// 确保手势容器可见且可交互
|
||||
if (self.bannerSwipeGestureContainer.hidden ||
|
||||
self.bannerLeftTapGestureContainer.hidden ||
|
||||
self.bannerRightTapGestureContainer.hidden) {
|
||||
|
||||
NSLog(@"🔧 检测到手势容器被隐藏,重新激活");
|
||||
[self restoreBannerGestureNormalMode];
|
||||
}
|
||||
|
||||
// 确保用户交互启用
|
||||
if (!self.bannerSwipeGestureContainer.userInteractionEnabled ||
|
||||
!self.bannerLeftTapGestureContainer.userInteractionEnabled ||
|
||||
!self.bannerRightTapGestureContainer.userInteractionEnabled) {
|
||||
|
||||
NSLog(@"🔧 检测到手势容器用户交互被禁用,重新启用");
|
||||
self.bannerSwipeGestureContainer.userInteractionEnabled = YES;
|
||||
self.bannerLeftTapGestureContainer.userInteractionEnabled = YES;
|
||||
self.bannerRightTapGestureContainer.userInteractionEnabled = YES;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
@@ -133,8 +133,10 @@ NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
|
||||
/// 更新其他 tag 的数据源,若传入空数组,则初始化并从 datasource 中获取数据
|
||||
- (void)updateAllDataSource:(NSArray *)datas {
|
||||
if (!datas || datas.count == 0) {
|
||||
self.datasource_chat = @[].mutableCopy;
|
||||
self.datasource_gift = @[].mutableCopy;
|
||||
// 清空分类数据源
|
||||
[self.datasource_chat removeAllObjects];
|
||||
[self.datasource_gift removeAllObjects];
|
||||
// 从主数据源重新构建分类数据源
|
||||
datas = self.datasource;
|
||||
}
|
||||
|
||||
@@ -152,6 +154,8 @@ NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
|
||||
case CustomMessageType_Treasure_Fairy:
|
||||
[self.datasource_gift addObject:model];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -178,8 +182,6 @@ NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
|
||||
- (void)appendAndScrollToAtUser {
|
||||
// 1. 检查 incomingMessages 是否为空
|
||||
if (self.incomingMessages.count < 1) {
|
||||
NSInteger rows = self.datasource.count;
|
||||
|
||||
// 2. 安全检查 locationArray 是否为空
|
||||
if (self.locationArray.count == 0) {
|
||||
[self scrollToBottomWithTipsHidden:YES];
|
||||
@@ -193,15 +195,22 @@ NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
|
||||
return;
|
||||
}
|
||||
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
|
||||
if (rows > indexPath.row) {
|
||||
[self.messageTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
|
||||
if (rows == indexPath.row + 1) {
|
||||
self.messageTipsBtn.hidden = YES;
|
||||
self.isPending = NO;
|
||||
}
|
||||
} else {
|
||||
// 将 datasource 的索引转换为当前显示数据源的索引
|
||||
NSInteger convertedIndex = [self convertDataSourceIndexToCurrentDisplayIndex:index];
|
||||
if (convertedIndex == NSNotFound) {
|
||||
[self scrollToBottomWithTipsHidden:YES];
|
||||
} else {
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:convertedIndex inSection:0];
|
||||
NSInteger currentRows = [self getCurrentDataSourceCount];
|
||||
if (currentRows > indexPath.row) {
|
||||
[self.messageTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
|
||||
if (currentRows == indexPath.row + 1) {
|
||||
self.messageTipsBtn.hidden = YES;
|
||||
self.isPending = NO;
|
||||
}
|
||||
} else {
|
||||
[self scrollToBottomWithTipsHidden:YES];
|
||||
}
|
||||
}
|
||||
|
||||
[self safelyRemoveLocationAtIndex:0];
|
||||
@@ -217,8 +226,10 @@ NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
|
||||
needReloadData = YES; // 标记需要重新加载数据
|
||||
}
|
||||
|
||||
// 5. 插入新消息
|
||||
NSMutableArray *indexPaths = @[].mutableCopy;
|
||||
// 5. 在更新数据源之前获取当前行数
|
||||
NSInteger currentRows = [self getCurrentDataSourceCount];
|
||||
|
||||
// 6. 插入新消息
|
||||
NSMutableArray *tempNewDatas = @[].mutableCopy;
|
||||
for (id item in self.incomingMessages) {
|
||||
XPMessageInfoModel *model = [self parseMessage:item];
|
||||
@@ -226,18 +237,32 @@ NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
|
||||
|
||||
[tempNewDatas addObject:model];
|
||||
[self.datasource addObject:model];
|
||||
[indexPaths addObject:[NSIndexPath indexPathForRow:self.datasource.count - 1 inSection:0]];
|
||||
|
||||
[self processAtMentionsForMessage:item];
|
||||
}
|
||||
[self updateAllDataSource:tempNewDatas];
|
||||
[self.incomingMessages removeAllObjects];
|
||||
|
||||
// 如果有删除操作,使用 reloadData;否则使用增量更新
|
||||
// 7. 更新 UITableView
|
||||
if (needReloadData) {
|
||||
[self.messageTableView reloadData];
|
||||
} else if (indexPaths.count > 0) {
|
||||
[self.messageTableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
|
||||
} else if (tempNewDatas.count > 0) {
|
||||
// 安全检查:确保数据源一致性
|
||||
NSInteger expectedRows = [self getCurrentDataSourceCount];
|
||||
if (expectedRows != [self.messageTableView numberOfRowsInSection:0]) {
|
||||
[self.messageTableView reloadData];
|
||||
} else {
|
||||
// 重新计算 indexPath,使用更新前的行数作为起始索引
|
||||
NSMutableArray *indexPaths = @[].mutableCopy;
|
||||
NSInteger startIndex = currentRows;
|
||||
if (startIndex >= 0 && startIndex <= [self.messageTableView numberOfRowsInSection:0]) {
|
||||
for (NSInteger i = 0; i < tempNewDatas.count; i++) {
|
||||
[indexPaths addObject:[NSIndexPath indexPathForRow:startIndex + i inSection:0]];
|
||||
}
|
||||
[self.messageTableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
|
||||
} else {
|
||||
[self.messageTableView reloadData];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 滚动到指定位置或底部
|
||||
@@ -245,9 +270,11 @@ NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
|
||||
}
|
||||
|
||||
- (void)scrollToBottomWithTipsHidden:(BOOL)hidden {
|
||||
NSInteger rows = self.datasource.count;
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:(rows - 1) inSection:0];
|
||||
[self.messageTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
|
||||
NSInteger rows = [self getCurrentDataSourceCount];
|
||||
if (rows > 0) {
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:(rows - 1) inSection:0];
|
||||
[self.messageTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
|
||||
}
|
||||
self.messageTipsBtn.hidden = hidden;
|
||||
self.isPending = NO;
|
||||
self.atCount = 0;
|
||||
@@ -296,9 +323,32 @@ NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
|
||||
|
||||
- (void)safelyRemoveMessages:(NSInteger)count {
|
||||
if (self.datasource.count >= count) {
|
||||
// 获取要删除的消息
|
||||
NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, count)];
|
||||
NSArray *removedMessages = [self.datasource objectsAtIndexes:set];
|
||||
|
||||
// 从主数据源删除
|
||||
[self.datasource removeObjectsAtIndexes:set];
|
||||
[self updateAllDataSource:nil];
|
||||
|
||||
// 从分类数据源中删除对应的消息
|
||||
for (XPMessageInfoModel *removedModel in removedMessages) {
|
||||
switch (removedModel.first) {
|
||||
case NIMMessageTypeText:
|
||||
case CustomMessageType_Face:
|
||||
[self.datasource_chat removeObject:removedModel];
|
||||
break;
|
||||
case CustomMessageType_Gift:
|
||||
case CustomMessageType_RoomBoom:
|
||||
case CustomMessageType_Candy_Tree:
|
||||
case CustomMessageType_Super_Gift:
|
||||
case CustomMessageType_AllMicroSend:
|
||||
case CustomMessageType_Treasure_Fairy:
|
||||
[self.datasource_gift removeObject:removedModel];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新 locationArray
|
||||
NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
|
||||
@@ -336,18 +386,81 @@ NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInteger)getCurrentDataSourceCount {
|
||||
NSInteger count = 0;
|
||||
switch (self.displayType) {
|
||||
case 1:
|
||||
count = self.datasource.count;
|
||||
break;
|
||||
case 2:
|
||||
count = self.datasource_chat.count;
|
||||
break;
|
||||
case 3:
|
||||
count = self.datasource_gift.count;
|
||||
break;
|
||||
default:
|
||||
count = self.datasource.count;
|
||||
break;
|
||||
}
|
||||
// 确保返回非负数
|
||||
return MAX(0, count);
|
||||
}
|
||||
|
||||
- (NSInteger)convertDataSourceIndexToCurrentDisplayIndex:(NSInteger)dataSourceIndex {
|
||||
if (self.displayType == 1) {
|
||||
return dataSourceIndex;
|
||||
}
|
||||
|
||||
if (dataSourceIndex >= self.datasource.count) {
|
||||
return NSNotFound;
|
||||
}
|
||||
|
||||
XPMessageInfoModel *targetModel = [self.datasource objectAtIndex:dataSourceIndex];
|
||||
if (!targetModel) {
|
||||
return NSNotFound;
|
||||
}
|
||||
|
||||
NSArray *currentDataSource = nil;
|
||||
switch (self.displayType) {
|
||||
case 2:
|
||||
currentDataSource = self.datasource_chat;
|
||||
break;
|
||||
case 3:
|
||||
currentDataSource = self.datasource_gift;
|
||||
break;
|
||||
default:
|
||||
return dataSourceIndex;
|
||||
}
|
||||
|
||||
// 在当前数据源中查找对应的模型
|
||||
for (NSInteger i = 0; i < currentDataSource.count; i++) {
|
||||
XPMessageInfoModel *model = [currentDataSource objectAtIndex:i];
|
||||
if ([model isEqual:targetModel]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return NSNotFound;
|
||||
}
|
||||
|
||||
- (void)scrollToFirstLocationOrBottom {
|
||||
NSInteger rows = self.datasource.count;
|
||||
if (self.locationArray.count == 0) {
|
||||
[self scrollToBottomWithTipsHidden:YES];
|
||||
return;
|
||||
}
|
||||
|
||||
NSInteger index = [self safeGetIndexFromLocationArrayAt:0];
|
||||
if (index == NSNotFound || index >= rows) {
|
||||
if (index == NSNotFound) {
|
||||
[self scrollToBottomWithTipsHidden:YES];
|
||||
return;
|
||||
}
|
||||
|
||||
// 将 datasource 的索引转换为当前显示数据源的索引
|
||||
NSInteger convertedIndex = [self convertDataSourceIndexToCurrentDisplayIndex:index];
|
||||
if (convertedIndex == NSNotFound) {
|
||||
[self scrollToBottomWithTipsHidden:YES];
|
||||
} else {
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:convertedIndex inSection:0];
|
||||
[self.messageTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
|
||||
[self safelyRemoveLocationAtIndex:0];
|
||||
}
|
||||
@@ -473,14 +586,15 @@ NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
|
||||
|
||||
BOOL needReloadData = NO;
|
||||
if (self.datasource.count > kRoomMessageMaxLength) {
|
||||
NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, kRoomMessageMaxLength/2)];
|
||||
NSArray *needRemoveMsgArray = [self.datasource objectsAtIndexes:set];
|
||||
[self.datasource removeObjectsInArray:needRemoveMsgArray];
|
||||
NSInteger removedCount = kRoomMessageMaxLength / 2;
|
||||
[self safelyRemoveMessages:removedCount];
|
||||
needReloadData = YES; // 标记需要重新加载数据
|
||||
}
|
||||
|
||||
// 在更新数据源之前获取当前行数
|
||||
NSInteger currentRows = [self getCurrentDataSourceCount];
|
||||
|
||||
NSMutableArray *tempArray = @[].mutableCopy;
|
||||
NSMutableArray *indexPaths = @[].mutableCopy;
|
||||
@kWeakify(self);
|
||||
[self.incomingMessages enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
@kStrongify(self);
|
||||
@@ -493,7 +607,6 @@ NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
|
||||
if (model) {
|
||||
[tempArray addObject:model];
|
||||
[self.datasource addObject:model];
|
||||
[indexPaths addObject:[NSIndexPath indexPathForRow:self.datasource.count - 1 inSection:0]];
|
||||
}
|
||||
}];
|
||||
|
||||
@@ -504,8 +617,24 @@ NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
|
||||
// 如果有删除操作,使用 reloadData;否则使用增量更新
|
||||
if (needReloadData) {
|
||||
[self.messageTableView reloadData];
|
||||
} else if (indexPaths.count > 0) {
|
||||
[self.messageTableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
|
||||
} else if (tempArray.count > 0) {
|
||||
// 安全检查:确保数据源一致性
|
||||
NSInteger expectedRows = [self getCurrentDataSourceCount];
|
||||
if (expectedRows != [self.messageTableView numberOfRowsInSection:0]) {
|
||||
[self.messageTableView reloadData];
|
||||
} else {
|
||||
// 重新计算 indexPath,使用更新前的行数作为起始索引
|
||||
NSMutableArray *indexPaths = @[].mutableCopy;
|
||||
NSInteger startIndex = currentRows;
|
||||
if (startIndex >= 0 && startIndex <= [self.messageTableView numberOfRowsInSection:0]) {
|
||||
for (NSInteger i = 0; i < tempArray.count; i++) {
|
||||
[indexPaths addObject:[NSIndexPath indexPathForRow:startIndex + i inSection:0]];
|
||||
}
|
||||
[self.messageTableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
|
||||
} else {
|
||||
[self.messageTableView reloadData];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//执行插入动画并滚动
|
||||
@@ -1340,21 +1469,23 @@ NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
NSInteger count = 0;
|
||||
switch (self.displayType) {
|
||||
case 1:
|
||||
return self.datasource.count;
|
||||
count = self.datasource.count;
|
||||
break;
|
||||
case 2:
|
||||
return self.datasource_chat.count;
|
||||
count = self.datasource_chat.count;
|
||||
break;
|
||||
case 3:
|
||||
return self.datasource_gift.count;
|
||||
count = self.datasource_gift.count;
|
||||
break;
|
||||
|
||||
default:
|
||||
return self.datasource.count;
|
||||
count = self.datasource.count;
|
||||
break;
|
||||
}
|
||||
// 确保返回非负数
|
||||
return MAX(0, count);
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
@@ -161,6 +161,13 @@
|
||||
self.isAnimating = NO;
|
||||
self.shouldStopAnimation = NO;
|
||||
|
||||
// 检查数据源
|
||||
if (self.source.count < 2) {
|
||||
NSLog(@"⚠️ PIGiftBravoGiftBroadcastView: 数据源不足 (%lu个),动画可能过快", (unsigned long)self.source.count);
|
||||
} else {
|
||||
NSLog(@"✅ PIGiftBravoGiftBroadcastView: 数据源正常 (%lu个)", (unsigned long)self.source.count);
|
||||
}
|
||||
|
||||
@kWeakify(self);
|
||||
[self.source enumerateObjectsUsingBlock:^(BravoGiftTabInfomationModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
@kStrongify(self);
|
||||
@@ -176,10 +183,13 @@
|
||||
|
||||
- (void)startLoop {
|
||||
if (self.isAnimating || self.labels.count == 0 || self.shouldStopAnimation) {
|
||||
NSLog(@"🚫 PIGiftBravoGiftBroadcastView: 动画启动被阻止 - isAnimating:%d, labelsCount:%lu, shouldStop:%d",
|
||||
self.isAnimating, (unsigned long)self.labels.count, self.shouldStopAnimation);
|
||||
return;
|
||||
}
|
||||
|
||||
self.isAnimating = YES;
|
||||
NSLog(@"🎬 PIGiftBravoGiftBroadcastView: 开始动画循环");
|
||||
[self playCurrentAnimation];
|
||||
}
|
||||
|
||||
@@ -187,21 +197,33 @@
|
||||
- (void)startAnimation {
|
||||
if (self.shouldStopAnimation) {
|
||||
self.shouldStopAnimation = NO;
|
||||
NSLog(@"🔄 PIGiftBravoGiftBroadcastView: 重置停止标志,重新开始动画");
|
||||
}
|
||||
|
||||
if (self.labels.count > 0) {
|
||||
[self startLoop];
|
||||
} else {
|
||||
NSLog(@"⚠️ PIGiftBravoGiftBroadcastView: 无法开始动画,labels为空");
|
||||
}
|
||||
}
|
||||
|
||||
// 公共方法:停止动画循环
|
||||
- (void)stopAnimation {
|
||||
NSLog(@"⏹️ PIGiftBravoGiftBroadcastView: 停止动画");
|
||||
[self endloop];
|
||||
}
|
||||
|
||||
- (void)playCurrentAnimation {
|
||||
// 更严格的状态检查
|
||||
if (self.shouldStopAnimation) {
|
||||
self.isAnimating = NO;
|
||||
NSLog(@"🚫 PIGiftBravoGiftBroadcastView: 动画被停止标志阻止");
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保只有一个动画在运行
|
||||
if (self.container.subviews.count > 0) {
|
||||
NSLog(@"⚠️ PIGiftBravoGiftBroadcastView: 容器中已有视图,跳过当前动画");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -209,9 +231,12 @@
|
||||
PIGiftBravoGiftBroadcastItemView *item = [self.labels xpSafeObjectAtIndex:self.index];
|
||||
if (!item) {
|
||||
self.isAnimating = NO;
|
||||
NSLog(@"❌ PIGiftBravoGiftBroadcastView: 无法获取当前索引(%ld)的视图", (long)self.index);
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"🎭 PIGiftBravoGiftBroadcastView: 播放第%ld个动画项", (long)self.index);
|
||||
|
||||
// 设置初始位置(屏幕右侧)
|
||||
item.frame = CGRectMake(KScreenWidth, 2, KScreenWidth - 25 - 42, 26);
|
||||
// 添加到容器视图
|
||||
@@ -226,11 +251,12 @@
|
||||
if (!finished || self.shouldStopAnimation) {
|
||||
[item removeFromSuperview];
|
||||
self.isAnimating = NO;
|
||||
NSLog(@"❌ PIGiftBravoGiftBroadcastView: 入场动画被中断");
|
||||
return;
|
||||
}
|
||||
|
||||
// 停留时间:2秒延迟后执行出场动画
|
||||
[UIView animateWithDuration:0.5 delay:2.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
|
||||
// 停留时间:从2秒增加到3秒
|
||||
[UIView animateWithDuration:0.5 delay:3.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
|
||||
item.frame = CGRectMake(-KScreenWidth, 2, KScreenWidth - 25 - 42, 26);
|
||||
} completion:^(BOOL finished) {
|
||||
@kStrongify(self);
|
||||
@@ -239,6 +265,7 @@
|
||||
|
||||
if (self.shouldStopAnimation) {
|
||||
self.isAnimating = NO;
|
||||
NSLog(@"⏹️ PIGiftBravoGiftBroadcastView: 出场动画被停止");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -246,10 +273,12 @@
|
||||
self.index += 1;
|
||||
if (self.index >= self.labels.count) {
|
||||
self.index = 0;
|
||||
NSLog(@"🔄 PIGiftBravoGiftBroadcastView: 动画循环重置到开始");
|
||||
}
|
||||
|
||||
// 使用定时器延迟下一个动画,避免递归调用
|
||||
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(scheduleNextAnimation) userInfo:nil repeats:NO];
|
||||
// 使用定时器延迟下一个动画,从0.1秒增加到0.5秒
|
||||
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(scheduleNextAnimation) userInfo:nil repeats:NO];
|
||||
NSLog(@"⏰ PIGiftBravoGiftBroadcastView: 安排下一个动画,延迟0.5秒");
|
||||
}];
|
||||
}];
|
||||
}
|
||||
@@ -260,9 +289,11 @@
|
||||
|
||||
if (self.shouldStopAnimation) {
|
||||
self.isAnimating = NO;
|
||||
NSLog(@"🚫 PIGiftBravoGiftBroadcastView: 定时器回调被停止标志阻止");
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"▶️ PIGiftBravoGiftBroadcastView: 定时器触发下一个动画");
|
||||
[self playCurrentAnimation];
|
||||
}
|
||||
|
||||
@@ -271,6 +302,8 @@
|
||||
self.shouldStopAnimation = YES;
|
||||
self.isAnimating = NO;
|
||||
|
||||
NSLog(@"🛑 PIGiftBravoGiftBroadcastView: 结束动画循环");
|
||||
|
||||
// 停止并清理定时器
|
||||
if (self.animationTimer) {
|
||||
[self.animationTimer invalidate];
|
||||
|
@@ -195,6 +195,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
/// 上麦请求弹窗定时器,用于10秒后自动移除弹窗
|
||||
@property(nonatomic,strong) NSTimer *upMicAskTimer;
|
||||
|
||||
/// 🔧 修复:保存 block 形式的通知观察者,防止内存泄漏
|
||||
@property(nonatomic,strong) id<NSObject> exchangeRoomAnimationViewObserver;
|
||||
|
||||
@end
|
||||
|
||||
@implementation XPRoomViewController
|
||||
@@ -310,6 +313,12 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
|
||||
[[RoomBoomManager sharedManager] removeEventListenerForTarget:self];
|
||||
|
||||
// 🔧 修复:移除 block 形式的通知观察者
|
||||
if (self.exchangeRoomAnimationViewObserver) {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self.exchangeRoomAnimationViewObserver];
|
||||
self.exchangeRoomAnimationViewObserver = nil;
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
// 🔧 修复:清理 RoomAnimationView
|
||||
@@ -451,7 +460,8 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
object:nil];
|
||||
|
||||
@kWeakify(self);
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:@"kExchangeRoomAnimationViewAndGameViewIndex"
|
||||
// 🔧 修复:保存 block 观察者的返回值,防止内存泄漏
|
||||
self.exchangeRoomAnimationViewObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"kExchangeRoomAnimationViewAndGameViewIndex"
|
||||
object:nil
|
||||
queue:NSOperationQueue.mainQueue
|
||||
usingBlock:^(NSNotification * _Nonnull notification) {
|
||||
@@ -1929,6 +1939,11 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
|
||||
NSLog(@"[Recv] --- Message Raw Attach Content: %@, %@, %ld", @(message.senderClientType), message.rawAttachContent, (long)message.messageType);
|
||||
|
||||
if ([message.rawAttachContent containsString:@"\"allRoomMsg\":1"]) {
|
||||
NSLog(@"[Recv] --- 拦截旧的全房间消息");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (message.messageType == NIMMessageTypeNotification) {
|
||||
[self handleNIMNotificationTypeMessage:message];
|
||||
} else if (message.messageType == NIMMessageTypeCustom) {
|
||||
|
130
docs/PIGiftBravoGiftBroadcastView_Animation_Fix.md
Normal file
130
docs/PIGiftBravoGiftBroadcastView_Animation_Fix.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# PIGiftBravoGiftBroadcastView 动画速度修复
|
||||
|
||||
## 问题描述
|
||||
`PIGiftBravoGiftBroadcastView` 中内容切换的动画变得非常快,影响用户体验。
|
||||
|
||||
## 问题分析
|
||||
|
||||
### 1. 动画时序问题
|
||||
- **原有时序**:
|
||||
- 入场动画:0.5秒
|
||||
- 停留时间:2.0秒延迟
|
||||
- 出场动画:0.5秒
|
||||
- 下一个动画间隔:0.1秒
|
||||
- **问题**:间隔时间过短,导致动画切换过快
|
||||
|
||||
### 2. 状态管理问题
|
||||
- 缺少严格的状态检查
|
||||
- 可能存在多个动画同时运行的情况
|
||||
- 状态同步不够完善
|
||||
|
||||
### 3. 数据源问题
|
||||
- 没有对数据源进行验证
|
||||
- 数据源不足时可能导致动画异常
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 1. 调整动画时序
|
||||
- **停留时间**:从2秒增加到3秒
|
||||
- **间隔时间**:从0.1秒增加到0.5秒
|
||||
- **总周期**:从3.1秒增加到4.5秒
|
||||
|
||||
### 2. 优化状态管理
|
||||
- 添加更严格的状态检查
|
||||
- 确保只有一个动画在运行
|
||||
- 防止容器中同时存在多个视图
|
||||
|
||||
### 3. 添加数据源检查
|
||||
- 在setupUI中验证数据源数量
|
||||
- 添加详细的日志输出便于调试
|
||||
|
||||
## 修改内容
|
||||
|
||||
### 文件:`PIGiftBravoGiftBroadcastView.m`
|
||||
|
||||
#### 1. setupUI方法
|
||||
```objc
|
||||
// 检查数据源
|
||||
if (self.source.count < 2) {
|
||||
NSLog(@"⚠️ PIGiftBravoGiftBroadcastView: 数据源不足 (%lu个),动画可能过快", (unsigned long)self.source.count);
|
||||
} else {
|
||||
NSLog(@"✅ PIGiftBravoGiftBroadcastView: 数据源正常 (%lu个)", (unsigned long)self.source.count);
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. playCurrentAnimation方法
|
||||
```objc
|
||||
// 更严格的状态检查
|
||||
if (self.shouldStopAnimation) {
|
||||
self.isAnimating = NO;
|
||||
NSLog(@"🚫 PIGiftBravoGiftBroadcastView: 动画被停止标志阻止");
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保只有一个动画在运行
|
||||
if (self.container.subviews.count > 0) {
|
||||
NSLog(@"⚠️ PIGiftBravoGiftBroadcastView: 容器中已有视图,跳过当前动画");
|
||||
return;
|
||||
}
|
||||
|
||||
// 停留时间:从2秒增加到3秒
|
||||
[UIView animateWithDuration:0.5 delay:3.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
|
||||
item.frame = CGRectMake(-KScreenWidth, 2, KScreenWidth - 25 - 42, 26);
|
||||
} completion:^(BOOL finished) {
|
||||
// 使用定时器延迟下一个动画,从0.1秒增加到0.5秒
|
||||
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(scheduleNextAnimation) userInfo:nil repeats:NO];
|
||||
NSLog(@"⏰ PIGiftBravoGiftBroadcastView: 安排下一个动画,延迟0.5秒");
|
||||
}];
|
||||
```
|
||||
|
||||
#### 3. 添加详细日志
|
||||
- 动画启动/停止状态
|
||||
- 数据源验证结果
|
||||
- 动画播放进度
|
||||
- 状态变化追踪
|
||||
|
||||
## 预期效果
|
||||
|
||||
### 1. 动画速度改善
|
||||
- 动画切换更加平滑自然
|
||||
- 用户有足够时间阅读内容
|
||||
- 减少视觉疲劳
|
||||
|
||||
### 2. 稳定性提升
|
||||
- 防止多个动画同时运行
|
||||
- 更好的状态管理
|
||||
- 减少异常情况
|
||||
|
||||
### 3. 调试便利性
|
||||
- 详细的日志输出
|
||||
- 便于问题定位
|
||||
- 性能监控
|
||||
|
||||
## 测试建议
|
||||
|
||||
### 1. 功能测试
|
||||
- 验证动画时序是否正确
|
||||
- 检查状态管理是否正常
|
||||
- 确认数据源检查是否有效
|
||||
|
||||
### 2. 性能测试
|
||||
- 监控内存使用情况
|
||||
- 检查CPU占用率
|
||||
- 验证动画流畅度
|
||||
|
||||
### 3. 边界测试
|
||||
- 数据源为空的情况
|
||||
- 快速切换场景的情况
|
||||
- 内存压力下的表现
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **向后兼容**:修改保持了原有的API接口不变
|
||||
2. **性能影响**:增加了日志输出,在Release版本中可以考虑移除
|
||||
3. **配置灵活**:动画时间可以通过常量定义,便于后续调整
|
||||
|
||||
## 修改时间
|
||||
2025年1月27日
|
||||
|
||||
## 修改人员
|
||||
AI Assistant
|
Reference in New Issue
Block a user