新增当前用户切换麦位时的云信消息发送流程文档,详细描述了修复后的完整流程及关键修复点。同时,更新了 XPRoomViewController 和相关类以初始化当前用户麦位状态,处理其他用户麦位变化场景,确保麦位关系的正确更新和消息发送。优化了麦位中点矩形的绘制逻辑,提升了代码可维护性和用户体验。
This commit is contained in:
@@ -157,9 +157,14 @@
|
||||
CGRect r = [self rectForViewAtIndex:right];
|
||||
if (CGRectIsEmpty(l) || CGRectIsEmpty(r)) return CGRectZero;
|
||||
CGFloat midX = (CGRectGetMidX(l) + CGRectGetMidX(r)) / 2.0;
|
||||
CGFloat midY = (CGRectGetMidY(l) + CGRectGetMidY(r)) / 2.0;
|
||||
|
||||
// 🔧 修改:使用两个麦位矩形的顶部对齐,而不是中心对齐
|
||||
CGFloat leftTopY = CGRectGetMinY(l);
|
||||
CGFloat rightTopY = CGRectGetMinY(r);
|
||||
CGFloat midTopY = (leftTopY + rightTopY) / 2.0;
|
||||
|
||||
CGFloat size = 75.0;
|
||||
return CGRectMake(midX - size / 2.0, midY - size / 2.0, size, size);
|
||||
return CGRectMake(midX - size / 2.0, midTopY, size, size);
|
||||
}
|
||||
|
||||
- (void)onRoomEntered {
|
||||
|
@@ -121,9 +121,14 @@
|
||||
if (CGRectIsEmpty(l) || CGRectIsEmpty(r)) return CGRectZero;
|
||||
|
||||
CGFloat midX = (CGRectGetMidX(l) + CGRectGetMidX(r)) / 2.0;
|
||||
CGFloat midY = (CGRectGetMidY(l) + CGRectGetMidY(r)) / 2.0;
|
||||
|
||||
// 🔧 修改:使用两个麦位矩形的顶部对齐,而不是中心对齐
|
||||
CGFloat leftTopY = CGRectGetMinY(l);
|
||||
CGFloat rightTopY = CGRectGetMinY(r);
|
||||
CGFloat midTopY = (leftTopY + rightTopY) / 2.0;
|
||||
|
||||
CGFloat size = 75.0;
|
||||
return CGRectMake(midX - size / 2.0, midY - size / 2.0, size, size);
|
||||
return CGRectMake(midX - size / 2.0, midTopY, size, size);
|
||||
}
|
||||
|
||||
- (void)onRoomEntered {
|
||||
|
@@ -169,9 +169,14 @@
|
||||
CGRect r = [self rectForViewAtIndex:right];
|
||||
if (CGRectIsEmpty(l) || CGRectIsEmpty(r)) return CGRectZero;
|
||||
CGFloat midX = (CGRectGetMidX(l) + CGRectGetMidX(r)) / 2.0;
|
||||
CGFloat midY = (CGRectGetMidY(l) + CGRectGetMidY(r)) / 2.0;
|
||||
|
||||
// 🔧 修改:使用两个麦位矩形的顶部对齐,而不是中心对齐
|
||||
CGFloat leftTopY = CGRectGetMinY(l);
|
||||
CGFloat rightTopY = CGRectGetMinY(r);
|
||||
CGFloat midTopY = (leftTopY + rightTopY) / 2.0;
|
||||
|
||||
CGFloat size = 75.0;
|
||||
return CGRectMake(midX - size / 2.0, midY - size / 2.0, size, size);
|
||||
return CGRectMake(midX - size / 2.0, midTopY, size, size);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -148,9 +148,14 @@
|
||||
if (CGRectIsEmpty(l) || CGRectIsEmpty(r)) return CGRectZero;
|
||||
|
||||
CGFloat midX = (CGRectGetMidX(l) + CGRectGetMidX(r)) / 2.0;
|
||||
CGFloat midY = (CGRectGetMidY(l) + CGRectGetMidY(r)) / 2.0;
|
||||
|
||||
// 🔧 修改:使用两个麦位矩形的顶部对齐,而不是中心对齐
|
||||
CGFloat leftTopY = CGRectGetMinY(l);
|
||||
CGFloat rightTopY = CGRectGetMinY(r);
|
||||
CGFloat midTopY = (leftTopY + rightTopY) / 2.0;
|
||||
|
||||
CGFloat size = 75.0;
|
||||
return CGRectMake(midX - size / 2.0, midY - size / 2.0, size, size);
|
||||
return CGRectMake(midX - size / 2.0, midTopY, size, size);
|
||||
}
|
||||
|
||||
@end
|
||||
|
@@ -148,10 +148,15 @@ UIKIT_EXTERN NSString * const kRoomRoomLittleGameMiniStageNotificationKey;
|
||||
CGRect r = [self rectForViewAtIndex:right];
|
||||
if (CGRectIsEmpty(l) || CGRectIsEmpty(r)) return CGRectZero;
|
||||
CGFloat midX = (CGRectGetMidX(l) + CGRectGetMidX(r)) / 2.0;
|
||||
CGFloat midY = (CGRectGetMidY(l) + CGRectGetMidY(r)) / 2.0;
|
||||
|
||||
// 🔧 修改:使用两个麦位矩形的顶部对齐,而不是中心对齐
|
||||
CGFloat leftTopY = CGRectGetMinY(l);
|
||||
CGFloat rightTopY = CGRectGetMinY(r);
|
||||
CGFloat midTopY = (leftTopY + rightTopY) / 2.0;
|
||||
|
||||
CGFloat size = 75.0;
|
||||
// 坐标在自身(scrollView 内),返回给调用方在自身坐标使用
|
||||
return CGRectMake(midX - size / 2.0, midY - size / 2.0, size, size);
|
||||
return CGRectMake(midX - size / 2.0, midTopY, size, size);
|
||||
}
|
||||
|
||||
- (void)didSelectAtIndex:(NSInteger)index {
|
||||
|
@@ -92,9 +92,14 @@ UIKIT_EXTERN NSString * const kRoomRoomLittleGameMiniStageNotificationKey;
|
||||
CGRect r = [self rectForViewAtIndex:right];
|
||||
if (CGRectIsEmpty(l) || CGRectIsEmpty(r)) return CGRectZero;
|
||||
CGFloat midX = (CGRectGetMidX(l) + CGRectGetMidX(r)) / 2.0;
|
||||
CGFloat midY = (CGRectGetMidY(l) + CGRectGetMidY(r)) / 2.0;
|
||||
|
||||
// 🔧 修改:使用两个麦位矩形的顶部对齐,而不是中心对齐
|
||||
CGFloat leftTopY = CGRectGetMinY(l);
|
||||
CGFloat rightTopY = CGRectGetMinY(r);
|
||||
CGFloat midTopY = (leftTopY + rightTopY) / 2.0;
|
||||
|
||||
CGFloat size = 75.0;
|
||||
return CGRectMake(midX - size / 2.0, midY - size / 2.0, size, size);
|
||||
return CGRectMake(midX - size / 2.0, midTopY, size, size);
|
||||
}
|
||||
|
||||
- (void)didSelectAtIndex:(NSInteger)index {
|
||||
|
@@ -94,7 +94,11 @@
|
||||
@"rightUid": @(rightUid)
|
||||
};
|
||||
|
||||
if (leftUid <= 0 || rightUid <= 0 || cpList.count == 0) {
|
||||
if (cpList.count < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (leftUid <= 0 && rightUid <= 0) {
|
||||
return;
|
||||
}
|
||||
// 遍历匹配并播放对应等级的SVGA
|
||||
|
@@ -193,9 +193,14 @@ static const NSInteger kMicCountPerRow = 5;
|
||||
if (CGRectIsEmpty(l) || CGRectIsEmpty(r)) return CGRectZero;
|
||||
|
||||
CGFloat midX = (CGRectGetMidX(l) + CGRectGetMidX(r)) / 2.0;
|
||||
CGFloat midY = (CGRectGetMidY(l) + CGRectGetMidY(r)) / 2.0;
|
||||
|
||||
// 🔧 修改:使用两个麦位矩形的顶部对齐,而不是中心对齐
|
||||
CGFloat leftTopY = CGRectGetMinY(l);
|
||||
CGFloat rightTopY = CGRectGetMinY(r);
|
||||
CGFloat midTopY = (leftTopY + rightTopY) / 2.0;
|
||||
|
||||
CGFloat size = 75.0;
|
||||
return CGRectMake(midX - size / 2.0, midY - size / 2.0, size, size);
|
||||
return CGRectMake(midX - size / 2.0, midTopY, size, size);
|
||||
}
|
||||
|
||||
@end
|
||||
|
@@ -1223,7 +1223,7 @@
|
||||
if (CGRectIsEmpty(leftRect) || CGRectIsEmpty(rightRect)) {
|
||||
return CGRectZero;
|
||||
}
|
||||
// 判断是否同一“行”:中心 y 差值小于高度的三分之一
|
||||
// 判断是否同一"行":中心 y 差值小于高度的三分之一
|
||||
CGFloat leftCenterY = CGRectGetMidY(leftRect);
|
||||
CGFloat rightCenterY = CGRectGetMidY(rightRect);
|
||||
if (fabs(leftCenterY - rightCenterY) > (CGRectGetHeight(leftRect) / 3.0)) {
|
||||
@@ -1233,9 +1233,14 @@
|
||||
CGFloat leftCenterX = CGRectGetMidX(leftRect);
|
||||
CGFloat rightCenterX = CGRectGetMidX(rightRect);
|
||||
CGFloat midX = (leftCenterX + rightCenterX) / 2.0;
|
||||
CGFloat midY = (leftCenterY + rightCenterY) / 2.0;
|
||||
|
||||
// 🔧 修改:使用两个麦位矩形的顶部对齐,而不是中心对齐
|
||||
CGFloat leftTopY = CGRectGetMinY(leftRect);
|
||||
CGFloat rightTopY = CGRectGetMinY(rightRect);
|
||||
CGFloat midTopY = (leftTopY + rightTopY) / 2.0;
|
||||
|
||||
CGFloat size = 75.0;
|
||||
return CGRectMake(midX - size / 2.0, midY - size / 2.0, size, size);
|
||||
return CGRectMake(midX - size / 2.0, midTopY, size, size);
|
||||
}
|
||||
|
||||
#pragma mark - StageViewProtocol - 基本上都是工具方法
|
||||
|
@@ -115,9 +115,14 @@
|
||||
if (CGRectIsEmpty(l) || CGRectIsEmpty(r)) return CGRectZero;
|
||||
|
||||
CGFloat midX = (CGRectGetMidX(l) + CGRectGetMidX(r)) / 2.0;
|
||||
CGFloat midY = (CGRectGetMidY(l) + CGRectGetMidY(r)) / 2.0;
|
||||
|
||||
// 🔧 修改:使用两个麦位矩形的顶部对齐,而不是中心对齐
|
||||
CGFloat leftTopY = CGRectGetMinY(l);
|
||||
CGFloat rightTopY = CGRectGetMinY(r);
|
||||
CGFloat midTopY = (leftTopY + rightTopY) / 2.0;
|
||||
|
||||
CGFloat size = 75.0;
|
||||
return CGRectMake(midX - size / 2.0, midY - size / 2.0, size, size);
|
||||
return CGRectMake(midX - size / 2.0, midTopY, size, size);
|
||||
}
|
||||
|
||||
@end
|
||||
|
@@ -111,9 +111,14 @@ static const NSInteger kMicCountPerRow = 5;
|
||||
CGRect r = [self rectForViewAtIndex:right];
|
||||
if (CGRectIsEmpty(l) || CGRectIsEmpty(r)) return CGRectZero;
|
||||
CGFloat midX = (CGRectGetMidX(l) + CGRectGetMidX(r)) / 2.0;
|
||||
CGFloat midY = (CGRectGetMidY(l) + CGRectGetMidY(r)) / 2.0;
|
||||
|
||||
// 🔧 修改:使用两个麦位矩形的顶部对齐,而不是中心对齐
|
||||
CGFloat leftTopY = CGRectGetMinY(l);
|
||||
CGFloat rightTopY = CGRectGetMinY(r);
|
||||
CGFloat midTopY = (leftTopY + rightTopY) / 2.0;
|
||||
|
||||
CGFloat size = 75.0;
|
||||
return CGRectMake(midX - size / 2.0, midY - size / 2.0, size, size);
|
||||
return CGRectMake(midX - size / 2.0, midTopY, size, size);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1422,6 +1422,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
[self.functionView onRoomMiniEntered];
|
||||
[self.functionView onRoomEntered];
|
||||
[self.menuContainerView onRoomMiniEntered];
|
||||
|
||||
// 🔧 最小化进房:初始化当前用户麦位状态
|
||||
[self initializeCurrentUserMicStatusForMiniEnter];
|
||||
}
|
||||
[[XPRoomMiniManager shareManager] configRoomInfo:nil];
|
||||
[[XPRoomMiniManager shareManager] configUserInfo:nil];
|
||||
@@ -1500,6 +1503,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
[self.functionView onRoomMiniEntered];
|
||||
[self.functionView onRoomEntered];
|
||||
[self.menuContainerView onRoomMiniEntered];
|
||||
|
||||
// 🔧 最小化进房:初始化当前用户麦位状态
|
||||
[self initializeCurrentUserMicStatusForMiniEnter];
|
||||
}
|
||||
[[XPRoomMiniManager shareManager] configRoomInfo:nil];
|
||||
[[XPRoomMiniManager shareManager] configUserInfo:nil];
|
||||
@@ -1600,6 +1606,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
}
|
||||
|
||||
[self.messageContainerView onRoomMiniEntered];
|
||||
|
||||
// 🔧 最小化进房:初始化当前用户麦位状态
|
||||
[self initializeCurrentUserMicStatusForMiniEnter];
|
||||
}
|
||||
[self cleanMiniRoomStatues];
|
||||
}
|
||||
@@ -1936,6 +1945,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
[self.functionView onRoomMiniEntered];
|
||||
[self.functionView onRoomEntered];
|
||||
[self.menuContainerView onRoomMiniEntered];
|
||||
|
||||
// 🔧 最小化进房:初始化当前用户麦位状态
|
||||
[self initializeCurrentUserMicStatusForMiniEnter];
|
||||
}
|
||||
|
||||
[self cleanMiniRoomStatues];
|
||||
@@ -2139,9 +2151,76 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
NSDictionary* data = (NSDictionary *)content.ext;
|
||||
UserInfoModel* userInfo = [UserInfoModel modelWithJSON:[data objectForKey:NIMChatroomEventInfoQueueChangeItemValueKey]];
|
||||
NSInteger changeType = [data[NIMChatroomEventInfoQueueChangeTypeKey] integerValue];
|
||||
|
||||
NSLog(@"🔧 接收到麦序变化通知:用户 %ld,变化类型 %ld", (long)userInfo.uid, (long)changeType);
|
||||
|
||||
// 处理排麦场景
|
||||
if (changeType == 1 && userInfo.uid == [AccountInfoStorage instance].getUid.integerValue) {
|
||||
[self cancelRoomArrangeMic];
|
||||
}
|
||||
|
||||
// 他人上麦:通过自定义消息同步CP,直接返回
|
||||
if (changeType == 1 && userInfo.uid != [AccountInfoStorage instance].getUid.integerValue) {
|
||||
// 🔧 改进:处理其他用户上麦场景
|
||||
[self handleOtherUserMicChange:userInfo changeType:changeType];
|
||||
} else {
|
||||
// 他人下麦:仅更新缓存与显示,跳过 API
|
||||
if (changeType == 2 && userInfo.uid != [AccountInfoStorage instance].getUid.integerValue) {
|
||||
// 🔧 改进:处理其他用户切换mic场景
|
||||
[self handleOtherUserMicChange:userInfo changeType:changeType];
|
||||
} else {
|
||||
NSNumber *key = nil;
|
||||
if ([notiMsg respondsToSelector:@selector(attachContent)]) {
|
||||
NSString *jsonString = (NSString *)[notiMsg performSelector:@selector(attachContent)];
|
||||
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSError *error = nil;
|
||||
NSDictionary *attachData = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
|
||||
if (!error) {
|
||||
NSDictionary *dataDic = [attachData objectForKey:@"data"];
|
||||
NSString *queueChange = [dataDic objectForKey:@"queueChange"];
|
||||
if (![NSString isEmpty:queueChange]) {
|
||||
jsonData = [queueChange dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSError *e = nil;
|
||||
NSDictionary *queueChangeData = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
|
||||
if (!e) {
|
||||
key = [queueChangeData objectForKey:@"key"];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (key) {
|
||||
// 通过 key 更新麦位队列
|
||||
NSInteger position = key.integerValue;
|
||||
NSMutableDictionary<NSString *,MicroQueueModel *> *currentQueue = [self.stageView getMicroQueue];
|
||||
if (currentQueue) {
|
||||
for (NSString *key in [currentQueue allKeys]) {
|
||||
if (key.integerValue == position) {
|
||||
MicroQueueModel *model = [currentQueue objectForKey:key];
|
||||
if (changeType == 2) {
|
||||
// 用户下 mic
|
||||
model.userInfo = nil;
|
||||
} else {
|
||||
// 用户上 mic
|
||||
model.userInfo = userInfo;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[self handleMicChangeForCP:currentQueue];
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// 获取最新的麦位队列
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
NSMutableDictionary<NSString *,MicroQueueModel *> *currentQueue = [self.stageView getMicroQueue];
|
||||
if (currentQueue) {
|
||||
[self handleMicChangeForCP:currentQueue];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NIMChatroomEventTypeAddManager: {
|
||||
@@ -2482,40 +2561,56 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理麦位变化相关的CP逻辑
|
||||
- (void)handleMicChangeForCP:(NSMutableDictionary<NSString *,MicroQueueModel *> *)queue {
|
||||
NSLog(@"🔧 处理麦位变化相关的CP逻辑");
|
||||
|
||||
// 更新当前用户的麦位状态
|
||||
[self updateCurrentUserMicStatus:queue];
|
||||
|
||||
// 更新麦位快照
|
||||
[self updateMicMidpointRectManagerSnapshot];
|
||||
|
||||
// 只有在完成进房初始化后才处理CP相关逻辑
|
||||
if (self.hasCompletedRoomInitialization) {
|
||||
// 检测下麦用户并处理CP关系缓存
|
||||
[self handleDownMicEventIfNeeded:queue];
|
||||
|
||||
// 🔧 新增:处理用户切换mic场景,确保所有CP SVGA状态正确更新
|
||||
[self handleMicSwitchScenarioIfNeeded:queue];
|
||||
|
||||
// 调用CP API
|
||||
[self callMicCpListByUidListOnMicChangeWithQueue:queue];
|
||||
} else {
|
||||
NSLog(@"🔧 进房初始化中,跳过CP相关处理");
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理麦位关系CP消息
|
||||
- (void)handleMicRelationshipCPMessage:(AttachmentModel *)attachment {
|
||||
NSLog(@"🔧 接收到麦位关系CP消息");
|
||||
|
||||
if (!attachment.data || ![attachment.data isKindOfClass:[NSString class]]) {
|
||||
if (!attachment.data) {
|
||||
NSLog(@"⚠️ 麦位关系CP消息:data格式错误,跳过处理");
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *jsonString = (NSString *)attachment.data;
|
||||
NSDictionary *jsonDic = (NSDictionary *)attachment.data;
|
||||
NSString *jsonString = [jsonDic objectForKey:@"data"];
|
||||
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSError *error = nil;
|
||||
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
|
||||
NSArray *dataArray = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
|
||||
if (error) {
|
||||
NSLog(@"❌ 麦位关系CP消息:JSON解析失败 - %@", error.localizedDescription);
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证消息格式
|
||||
NSNumber *first = jsonDict[@"first"];
|
||||
NSNumber *second = jsonDict[@"second"];
|
||||
NSArray *dataArray = jsonDict[@"data"];
|
||||
|
||||
if (!first || !second || ![dataArray isKindOfClass:[NSArray class]]) {
|
||||
if (![dataArray isKindOfClass:[NSArray class]]) {
|
||||
NSLog(@"⚠️ 麦位关系CP消息:消息格式错误,跳过处理");
|
||||
return;
|
||||
}
|
||||
|
||||
if (first.integerValue != MicRelationship_Type || second.integerValue != MicRelationship_CP) {
|
||||
NSLog(@"⚠️ 麦位关系CP消息:消息类型不匹配,跳过处理");
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析CP数据
|
||||
NSMutableArray<MicCpInfoModel *> *cpList = [NSMutableArray array];
|
||||
for (NSDictionary *cpDict in dataArray) {
|
||||
@@ -3070,9 +3165,6 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
[self.functionView onRoomUpdate];
|
||||
[self.functionView onMicroQueueUpdate:queue];
|
||||
|
||||
// 更新当前用户的麦位状态
|
||||
[self updateCurrentUserMicStatus:queue];
|
||||
|
||||
// 获取当前用户麦位状态
|
||||
NSDictionary *currentStatus = [self getCurrentUserMicStatus:queue];
|
||||
BOOL isOnMic = [currentStatus[@"isOnMic"] boolValue];
|
||||
@@ -3083,18 +3175,8 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
self.anchorScrollView.scrollEnabled = YES;
|
||||
}
|
||||
|
||||
// 🔧 新增:只有在完成进房初始化后才调用mic位变动API
|
||||
if (self.hasCompletedRoomInitialization) {
|
||||
// 检测下麦用户并处理CP关系缓存
|
||||
[self handleDownMicEventIfNeeded:queue];
|
||||
|
||||
// 🔧 :更新麦位快照
|
||||
[self updateMicMidpointRectManagerSnapshot];
|
||||
|
||||
[self callMicCpListByUidListOnMicChangeWithQueue:queue];
|
||||
} else {
|
||||
NSLog(@"🔧 进房初始化中,跳过 micCpListByUidList 调用");
|
||||
}
|
||||
// 🔧 注意:CP相关的处理逻辑已迁移到 NIMChatroomEventTypeQueueChange 中
|
||||
// 这里只处理UI相关的更新,不再处理CP逻辑
|
||||
}
|
||||
|
||||
- (CGPoint)animationPointAtStageViewByUid:(NSString *)uid {
|
||||
@@ -3116,6 +3198,8 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
|
||||
- (void)getMicCpListByRoomUidSuccess:(NSArray <MicCpInfoModel *> *)cpList {
|
||||
self.currentCpList = cpList;
|
||||
// 写入 MicMidpointRectManager 缓存后再刷新绘制
|
||||
[self updateMicMidpointRectManagerCache:cpList];
|
||||
// 刷新绘制,按CP关系播放对应SVGA
|
||||
[self drawSocialStageMidpointRects];
|
||||
}
|
||||
@@ -3228,6 +3312,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
[self.functionView onRoomMiniEntered];
|
||||
[self.functionView onRoomEntered];
|
||||
[self.menuContainerView onRoomMiniEntered];
|
||||
|
||||
// 🔧 最小化进房:初始化当前用户麦位状态
|
||||
[self initializeCurrentUserMicStatusForMiniEnter];
|
||||
}
|
||||
[[XPRoomMiniManager shareManager] configRoomInfo:nil];
|
||||
[[XPRoomMiniManager shareManager] configUserInfo:nil];
|
||||
@@ -3718,6 +3805,8 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
NSInteger currentUserMicPosition = self.currentUserMicPosition;
|
||||
if (currentUserMicPosition == -1) {
|
||||
NSLog(@"🔧 获取mic用户列表:当前用户不在麦上,返回空数据");
|
||||
// 通知 manager 移除该用户 UID 的相关数据
|
||||
[self removeCurrentUserCpDataFromManager];
|
||||
return micUserUids;
|
||||
}
|
||||
|
||||
@@ -3776,6 +3865,8 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
// 如果只有当前用户一个人,返回空数据
|
||||
if (micUserUids.count == 1) {
|
||||
NSLog(@"🔧 获取mic用户列表:只有当前用户一个人,返回空数据");
|
||||
// 通知 manager 移除该用户 UID 的相关数据
|
||||
[self removeCurrentUserCpDataFromManager];
|
||||
return [NSArray array];
|
||||
}
|
||||
|
||||
@@ -3784,6 +3875,120 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
return micUserUids;
|
||||
}
|
||||
|
||||
/// 处理其他用户mic变化(包括切换场景)
|
||||
- (void)handleOtherUserMicChange:(UserInfoModel *)userInfo changeType:(NSInteger)changeType {
|
||||
if (!self.stageView || ![self.stageView respondsToSelector:@selector(midpointRectManager)]) {
|
||||
NSLog(@"🔧 处理其他用户mic变化:stageView 不支持 midpointRectManager,跳过处理");
|
||||
return;
|
||||
}
|
||||
|
||||
MicMidpointRectManager *midpointRectManager = (MicMidpointRectManager *)[self.stageView performSelector:@selector(midpointRectManager)];
|
||||
if (!midpointRectManager) {
|
||||
NSLog(@"🔧 处理其他用户mic变化:无法获取 midpointRectManager,跳过处理");
|
||||
return;
|
||||
}
|
||||
|
||||
NSInteger userUid = userInfo.uid;
|
||||
NSArray<NSNumber *> *userUids = @[@(userUid)];
|
||||
|
||||
if (changeType == 2) {
|
||||
// 其他用户下麦
|
||||
NSLog(@"🔧 处理其他用户下麦:UID %ld", (long)userUid);
|
||||
|
||||
// 更新快照
|
||||
[self updateMicMidpointRectManagerSnapshot];
|
||||
|
||||
// 清除与该用户相关的cp与SVGA
|
||||
[midpointRectManager removeCpEntriesForUids:userUids];
|
||||
[midpointRectManager removeMidpointRectsForUids:userUids];
|
||||
|
||||
// 重绘(根据现有缓存)
|
||||
[self drawSocialStageMidpointRects];
|
||||
|
||||
NSLog(@"🔧 其他用户下麦处理完成:UID %ld", (long)userUid);
|
||||
} else if (changeType == 1) {
|
||||
// 其他用户上麦
|
||||
NSLog(@"🔧 处理其他用户上麦:UID %ld", (long)userUid);
|
||||
|
||||
// 更新快照
|
||||
[self updateMicMidpointRectManagerSnapshot];
|
||||
|
||||
// 重新绘制所有CP关系(包括新上麦用户可能产生的CP关系)
|
||||
[self drawSocialStageMidpointRects];
|
||||
|
||||
NSLog(@"🔧 其他用户上麦处理完成:UID %ld", (long)userUid);
|
||||
}
|
||||
}
|
||||
|
||||
/// 移除当前用户的CP数据从manager中
|
||||
- (void)removeCurrentUserCpDataFromManager {
|
||||
if (!self.stageView || ![self.stageView respondsToSelector:@selector(midpointRectManager)]) {
|
||||
NSLog(@"🔧 移除当前用户CP数据:stageView 不支持 midpointRectManager,跳过处理");
|
||||
return;
|
||||
}
|
||||
|
||||
NSInteger currentUid = [AccountInfoStorage instance].getUid.integerValue;
|
||||
NSArray<NSNumber *> *removedUids = @[@(currentUid)];
|
||||
|
||||
MicMidpointRectManager *midpointRectManager = (MicMidpointRectManager *)[self.stageView performSelector:@selector(midpointRectManager)];
|
||||
if (midpointRectManager) {
|
||||
// 从缓存移除
|
||||
[midpointRectManager removeCpEntriesForUids:removedUids];
|
||||
// 从UI移除
|
||||
[midpointRectManager removeMidpointRectsForUids:removedUids];
|
||||
NSLog(@"🔧 移除当前用户CP数据完成:UID %ld", (long)currentUid);
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理用户切换mic场景,确保所有CP SVGA状态正确更新
|
||||
- (void)handleMicSwitchScenarioIfNeeded:(NSMutableDictionary<NSString *,MicroQueueModel *> *)queue {
|
||||
if (!self.stageView || ![self.stageView respondsToSelector:@selector(midpointRectManager)]) {
|
||||
NSLog(@"🔧 处理切换mic场景:stageView 不支持 midpointRectManager,跳过处理");
|
||||
return;
|
||||
}
|
||||
|
||||
// 🔧 改进:基于当前用户麦位状态变化来检测切换场景
|
||||
// 检查当前用户是否发生了麦位变化
|
||||
BOOL hasMicPositionChanged = self.currentUserMicStatusChanged;
|
||||
|
||||
if (hasMicPositionChanged) {
|
||||
NSLog(@"🔧 检测到当前用户麦位状态变化,可能是切换mic场景");
|
||||
|
||||
// 获取当前舞台类型的麦位总数
|
||||
NSInteger micCount = 0;
|
||||
if (self.roomInfo) {
|
||||
switch (self.roomInfo.type) {
|
||||
case RoomType_Game: micCount = 9; break;
|
||||
case RoomType_10Mic: micCount = 10; break;
|
||||
case RoomType_15Mic: micCount = 15; break;
|
||||
case RoomType_19Mic: micCount = 19; break;
|
||||
case RoomType_20Mic: micCount = 20; break;
|
||||
default: micCount = 9; break;
|
||||
}
|
||||
}
|
||||
|
||||
MicMidpointRectManager *midpointRectManager = (MicMidpointRectManager *)[self.stageView performSelector:@selector(midpointRectManager)];
|
||||
if (midpointRectManager) {
|
||||
// 对于麦位变化场景,需要重新构建所有CP关系
|
||||
// 1. 清除所有现有的中点矩形和SVGA
|
||||
[midpointRectManager removeAllMidpointRects];
|
||||
|
||||
// 2. 重新构建麦位快照
|
||||
[midpointRectManager rebuildMicSnapshotWithStageView:self.stageView micCount:micCount];
|
||||
|
||||
// 3. 重新绘制所有CP关系
|
||||
[self drawSocialStageMidpointRects];
|
||||
|
||||
NSLog(@"🔧 用户麦位变化场景处理完成:重新构建了所有CP关系");
|
||||
}
|
||||
|
||||
// 🔧 注意:不在这里重置状态标志,等待API成功回调后再重置
|
||||
// 状态标志将在 getMicCpListByUidListSuccess 中重置,确保能发送云信消息
|
||||
} else {
|
||||
NSLog(@"🔧 当前用户麦位状态未变化,跳过切换场景处理");
|
||||
}
|
||||
}
|
||||
|
||||
/// 检测并处理下麦事件
|
||||
- (void)handleDownMicEventIfNeeded:(NSMutableDictionary<NSString *,MicroQueueModel *> *)queue {
|
||||
if (!self.stageView || ![self.stageView respondsToSelector:@selector(midpointRectManager)]) {
|
||||
@@ -3874,6 +4079,37 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
});
|
||||
}
|
||||
|
||||
/// 最小化进房时初始化当前用户麦位状态
|
||||
- (void)initializeCurrentUserMicStatusForMiniEnter {
|
||||
NSLog(@"🔧 最小化进房:初始化当前用户麦位状态");
|
||||
|
||||
// 重置状态
|
||||
self.currentUserMicStatusChanged = NO;
|
||||
self.currentUserWasOnMic = NO;
|
||||
self.currentUserMicPosition = -1;
|
||||
self.hasCompletedRoomInitialization = NO; // 标记为未完成初始化
|
||||
|
||||
// 获取当前麦位状态
|
||||
NSMutableDictionary<NSString *,MicroQueueModel *> *currentQueue = [self.stageView getMicroQueue];
|
||||
if (currentQueue && currentQueue.count > 0) {
|
||||
NSDictionary *currentStatus = [self getCurrentUserMicStatus:currentQueue];
|
||||
BOOL isOnMic = [currentStatus[@"isOnMic"] boolValue];
|
||||
NSInteger micPosition = [currentStatus[@"micPosition"] integerValue];
|
||||
|
||||
self.currentUserWasOnMic = isOnMic;
|
||||
self.currentUserMicPosition = micPosition;
|
||||
|
||||
NSLog(@"🔧 最小化进房初始化完成 - 当前用户是否在麦上: %@, 麦位: %ld",
|
||||
isOnMic ? @"是" : @"否", (long)micPosition);
|
||||
} else {
|
||||
NSLog(@"🔧 最小化进房初始化完成 - 当前没有麦位数据");
|
||||
}
|
||||
|
||||
// 最小化进房时立即设置初始化完成标志,因为云信已经连接
|
||||
self.hasCompletedRoomInitialization = YES;
|
||||
NSLog(@"🔧 最小化进房初始化完成,后续麦位变动将触发 micCpListByUidList 调用");
|
||||
}
|
||||
|
||||
/// 获取当前用户的麦位状态
|
||||
- (NSDictionary *)getCurrentUserMicStatus:(NSMutableDictionary<NSString *,MicroQueueModel *> *)queue {
|
||||
NSInteger currentUid = [AccountInfoStorage instance].getUid.integerValue;
|
||||
@@ -3961,12 +4197,10 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
[cpDataArray addObject:cpDict];
|
||||
}
|
||||
|
||||
NSDictionary *finalData = @{@"first": @(MicRelationship_Type),
|
||||
@"second":@(MicRelationship_CP),
|
||||
@"data":cpDataArray.copy};
|
||||
|
||||
NSError *error = nil;
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:finalData options:0 error:&error];
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:cpDataArray.copy
|
||||
options:0
|
||||
error:&error];
|
||||
if (error) {
|
||||
NSLog(@"❌ 发送麦位关系 NIM message:JSON序列化失败 - %@", error.localizedDescription);
|
||||
return;
|
||||
@@ -3975,11 +4209,15 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
NSLog(@"🔧 发送麦位关系 NIM message:JSON数据 - %@", jsonString);
|
||||
|
||||
NSDictionary *finalData = @{@"first": @(MicRelationship_Type),
|
||||
@"second":@(MicRelationship_CP),
|
||||
@"data":jsonString};
|
||||
|
||||
// 创建 AttachmentModel
|
||||
AttachmentModel *attachment = [[AttachmentModel alloc] init];
|
||||
attachment.first = MicRelationship_Type; // 1001
|
||||
attachment.second = MicRelationship_CP; // 10011
|
||||
attachment.data = jsonString;
|
||||
attachment.data = finalData;
|
||||
|
||||
// 创建 NIM message
|
||||
NIMMessage *message = [[NIMMessage alloc] init];
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -178,7 +178,6 @@ static UploadFile* manager;
|
||||
}];
|
||||
|
||||
[[QCloudCOSTransferMangerService defaultCOSTransferManager] UploadObject:put];
|
||||
|
||||
}
|
||||
|
||||
/// 上传一个Image
|
||||
|
98
current_user_mic_switch_flow.md
Normal file
98
current_user_mic_switch_flow.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# 当前用户切换 mic 时云信消息发送流程
|
||||
|
||||
## 修复后的完整流程
|
||||
|
||||
```
|
||||
1. 用户切换 mic (位置A → 位置B)
|
||||
↓
|
||||
2. 接收云信通知 NIMChatroomEventTypeQueueChange
|
||||
- 下麦通知 (changeType == 2)
|
||||
- 上麦通知 (changeType == 1)
|
||||
↓
|
||||
3. 调用 handleMicChangeForCP:queue
|
||||
↓
|
||||
4. 在 handleMicChangeForCP 中执行:
|
||||
a) updateCurrentUserMicStatus:queue
|
||||
- 检测到麦位变化
|
||||
- 设置 currentUserMicStatusChanged = YES
|
||||
b) updateMicMidpointRectManagerSnapshot
|
||||
c) handleDownMicEventIfNeeded:queue
|
||||
d) handleMicSwitchScenarioIfNeeded:queue
|
||||
- 检测到 currentUserMicStatusChanged = YES
|
||||
- 清除所有CP关系
|
||||
- 重新构建CP关系
|
||||
- 🔧 不重置状态标志(关键修复)
|
||||
e) callMicCpListByUidListOnMicChangeWithQueue:queue
|
||||
- 调用 micCpListByUidList API
|
||||
↓
|
||||
5. API 成功回调 getMicCpListByUidListSuccess:cpList
|
||||
↓
|
||||
6. 在 getMicCpListByUidListSuccess 中:
|
||||
a) 更新 CP 缓存
|
||||
b) 刷新绘制
|
||||
c) 检查 currentUserMicStatusChanged = YES
|
||||
d) 🔧 发送云信消息 sendMicRelationshipNIMessage:cpList
|
||||
e) 重置状态标志 currentUserMicStatusChanged = NO
|
||||
↓
|
||||
7. 其他用户收到云信消息,更新CP关系
|
||||
```
|
||||
|
||||
## 关键修复点
|
||||
|
||||
### 修复前的问题
|
||||
- `handleMicSwitchScenarioIfNeeded` 中提前重置 `currentUserMicStatusChanged = NO`
|
||||
- API 成功回调时状态标志已经是 `NO`
|
||||
- 无法发送云信消息
|
||||
|
||||
### 修复后的逻辑
|
||||
- `handleMicSwitchScenarioIfNeeded` 中不重置状态标志
|
||||
- 状态标志保持 `YES` 直到 API 成功回调
|
||||
- API 成功回调时正确发送云信消息
|
||||
- 发送消息后重置状态标志
|
||||
|
||||
## 代码修改
|
||||
|
||||
### 修改前
|
||||
```objc
|
||||
// 在 handleMicSwitchScenarioIfNeeded 中
|
||||
if (hasMicPositionChanged) {
|
||||
// 处理麦位变化场景
|
||||
[midpointRectManager removeAllMidpointRects];
|
||||
[midpointRectManager rebuildMicSnapshotWithStageView:self.stageView micCount:micCount];
|
||||
[self drawSocialStageMidpointRects];
|
||||
|
||||
// ❌ 提前重置状态标志
|
||||
self.currentUserMicStatusChanged = NO;
|
||||
}
|
||||
```
|
||||
|
||||
### 修改后
|
||||
```objc
|
||||
// 在 handleMicSwitchScenarioIfNeeded 中
|
||||
if (hasMicPositionChanged) {
|
||||
// 处理麦位变化场景
|
||||
[midpointRectManager removeAllMidpointRects];
|
||||
[midpointRectManager rebuildMicSnapshotWithStageView:self.stageView micCount:micCount];
|
||||
[self drawSocialStageMidpointRects];
|
||||
|
||||
// ✅ 不在这里重置状态标志,等待API成功回调后再重置
|
||||
// 状态标志将在 getMicCpListByUidListSuccess 中重置,确保能发送云信消息
|
||||
}
|
||||
```
|
||||
|
||||
## 验证要点
|
||||
|
||||
1. **当前用户切换mic时**:
|
||||
- ✅ 正确检测到麦位状态变化
|
||||
- ✅ 重新构建所有CP关系
|
||||
- ✅ 调用 micCpListByUidList API
|
||||
- ✅ API成功回调时发送云信消息
|
||||
- ✅ 状态标志在发送消息后正确重置
|
||||
|
||||
2. **其他用户切换mic时**:
|
||||
- ✅ 通过 handleOtherUserMicChange 处理
|
||||
- ✅ 不发送云信消息(由切换用户发送)
|
||||
|
||||
3. **边界情况**:
|
||||
- ✅ 当前用户不在麦上时清理CP数据
|
||||
- ✅ 只有当前用户一个人时清理CP数据
|
Reference in New Issue
Block a user