更新房间功能,新增 MicCpInfoModel 类以管理麦位关系数据,优化房间中点矩形和麦位状态管理逻辑。同时,更新相关 Presenter 和 ViewController 以集成新的麦位关系 API,提升代码可维护性和用户体验。新增麦位状态监测和更新机制,确保用户在房间中的麦位信息实时准确。
This commit is contained in:
@@ -554,6 +554,7 @@
|
||||
4C886BEB2E014AE5006F0BA7 /* MedalsPresenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C886BEA2E014AE5006F0BA7 /* MedalsPresenter.m */; };
|
||||
4C886BEE2E014B6C006F0BA7 /* Api+Medals.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C886BED2E014B6C006F0BA7 /* Api+Medals.m */; };
|
||||
4C886BF22E015D61006F0BA7 /* MedalsModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C886BF12E015D61006F0BA7 /* MedalsModel.m */; };
|
||||
4C9828132E6EB50000FC6345 /* MicCpInfoModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C9828122E6EB50000FC6345 /* MicCpInfoModel.m */; };
|
||||
4CA532B42D5AEE9400B8F59F /* Api+LuckyPackage.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CA532B32D5AEE9400B8F59F /* Api+LuckyPackage.m */; };
|
||||
4CA532B72D5B333200B8F59F /* RoomLuckyPackageInfoModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CA532B62D5B333200B8F59F /* RoomLuckyPackageInfoModel.m */; };
|
||||
4CA532BA2D5C8EBE00B8F59F /* LuckyPackageBannerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CA532B92D5C8EBE00B8F59F /* LuckyPackageBannerView.m */; };
|
||||
@@ -2783,6 +2784,8 @@
|
||||
4C886BED2E014B6C006F0BA7 /* Api+Medals.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "Api+Medals.m"; sourceTree = "<group>"; };
|
||||
4C886BF02E015D61006F0BA7 /* MedalsModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MedalsModel.h; sourceTree = "<group>"; };
|
||||
4C886BF12E015D61006F0BA7 /* MedalsModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MedalsModel.m; sourceTree = "<group>"; };
|
||||
4C9828112E6EB50000FC6345 /* MicCpInfoModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MicCpInfoModel.h; sourceTree = "<group>"; };
|
||||
4C9828122E6EB50000FC6345 /* MicCpInfoModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MicCpInfoModel.m; sourceTree = "<group>"; };
|
||||
4CA532B22D5AEE9400B8F59F /* Api+LuckyPackage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Api+LuckyPackage.h"; sourceTree = "<group>"; };
|
||||
4CA532B32D5AEE9400B8F59F /* Api+LuckyPackage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "Api+LuckyPackage.m"; sourceTree = "<group>"; };
|
||||
4CA532B52D5B333200B8F59F /* RoomLuckyPackageInfoModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RoomLuckyPackageInfoModel.h; sourceTree = "<group>"; };
|
||||
@@ -7923,6 +7926,8 @@
|
||||
4C75CEFA2D6318FF009147A5 /* RoomEnterModel.m */,
|
||||
4CFBE0C82DAD085700A923AF /* BravoGiftTabInfomationModel.h */,
|
||||
4CFBE0C92DAD085700A923AF /* BravoGiftTabInfomationModel.m */,
|
||||
4C9828112E6EB50000FC6345 /* MicCpInfoModel.h */,
|
||||
4C9828122E6EB50000FC6345 /* MicCpInfoModel.m */,
|
||||
);
|
||||
path = Model;
|
||||
sourceTree = "<group>";
|
||||
@@ -12738,6 +12743,7 @@
|
||||
E82325F2274E2DE6003A3332 /* XPUserCardViewController.m in Sources */,
|
||||
4CA532B72D5B333200B8F59F /* RoomLuckyPackageInfoModel.m in Sources */,
|
||||
E85E7B512A4EB0D300B6D00A /* Api+Guild.m in Sources */,
|
||||
4C9828132E6EB50000FC6345 /* MicCpInfoModel.m in Sources */,
|
||||
E83645682A40A2DC00E0DBE4 /* XPSkillCardPlayerManager.m in Sources */,
|
||||
E8F65C222869A36F009BB5B9 /* ContentShareMonentsModel.m in Sources */,
|
||||
9B6E856E281AABAB0041A321 /* XPRoomRecommendModel.m in Sources */,
|
||||
|
@@ -160,9 +160,6 @@ UIKIT_EXTERN NSString * adImageName;
|
||||
info.identifier = emotionDic[@"id"];
|
||||
info.image = image;
|
||||
|
||||
// 添加调试日志
|
||||
NSLog(@"加载表情: %@, 图片: %@, 是否成功: %@", info.displayName, emotionDic[@"file"], image ? @"是" : @"否");
|
||||
|
||||
[array addObject:info];
|
||||
}
|
||||
//在这里强烈建议先预加载一下表情
|
||||
@@ -171,16 +168,7 @@ UIKIT_EXTERN NSString * adImageName;
|
||||
|
||||
// 清理 emoji 缓存,确保新的尺寸设置生效
|
||||
[QEmotionHelper clearEmojiCache];
|
||||
|
||||
#if DEBUG
|
||||
// 测试图片加载
|
||||
NSLog(@"表情数组加载完成,总数: %lu", (unsigned long)array.count);
|
||||
for (int i = 0; i < MIN(array.count, 3); i++) {
|
||||
QEmotion *emotion = array[i];
|
||||
NSLog(@"测试表情 %d: %@, 图片: %@", i, emotion.displayName, emotion.image ? @"加载成功" : @"加载失败");
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma mark - 广告
|
||||
|
@@ -156,13 +156,19 @@ typedef NS_ENUM(NSUInteger, CustomMessageType) {
|
||||
|
||||
/// 客户端独立收发的消息, 从 1000 开始
|
||||
ClientMessage_Type = 1000,
|
||||
|
||||
MicRelationship_Type = 1001,
|
||||
};
|
||||
|
||||
|
||||
typedef NS_ENUM(NSUInteger, ClientMessageType) {
|
||||
ClientMessage_UpMic_Ask = 1001,
|
||||
ClientMessage_UpMic_Agree = 1002,
|
||||
ClientMessage_UpMic_Reject = 1003,
|
||||
ClientMessage_UpMic_Ask = 10001,
|
||||
ClientMessage_UpMic_Agree = 10002,
|
||||
ClientMessage_UpMic_Reject = 10003,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger, MicRelationshipType) {
|
||||
MicRelationship_CP = 10011,
|
||||
};
|
||||
|
||||
|
||||
|
@@ -163,6 +163,9 @@ UIKIT_EXTERN NSString *kRequestTicket;
|
||||
// self.isHavePermission = YES;
|
||||
// return;
|
||||
//#endif
|
||||
|
||||
// TODO: 切换账号可能会保持权限状态,需要进一步检查逻辑
|
||||
|
||||
ClientConfig *config = [ClientConfig shareConfig];
|
||||
NSArray *uidList = config.configInfo.giveDiamondErbanNoList;
|
||||
for (id uid in uidList) {
|
||||
|
@@ -232,6 +232,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
+ (void)shareGen:(HttpRequestHelperCompletion)completion targetUid:(NSString *)targetUid;
|
||||
|
||||
+ (void)getRoomMicCpListByRoomUid:(HttpRequestHelperCompletion)completion roomUid:(NSString *)roomUid;
|
||||
|
||||
+ (void)getRoomMicCpListByUidList:(HttpRequestHelperCompletion)completion uidList:(NSString *)uidList;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@@ -322,4 +322,13 @@
|
||||
+ (void)shareGen:(HttpRequestHelperCompletion)completion targetUid:(NSString *)targetUid {
|
||||
[self makeRequest:@"share/gen" method:HttpRequestHelperMethodPOST completion:completion, __FUNCTION__, targetUid, nil];
|
||||
}
|
||||
|
||||
+ (void)getRoomMicCpListByRoomUid:(HttpRequestHelperCompletion)completion roomUid:(NSString *)roomUid {
|
||||
[self makeRequest:@"room/mic/cp/listByRoomUid" method:HttpRequestHelperMethodGET completion:completion, __FUNCTION__, roomUid, nil];
|
||||
}
|
||||
|
||||
+ (void)getRoomMicCpListByUidList:(HttpRequestHelperCompletion)completion uidList:(NSArray<NSString *> *)uidList {
|
||||
[self makeRequest:@"room/mic/cp/listByUidList" method:HttpRequestHelperMethodGET completion:completion, __FUNCTION__, uidList, nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@@ -133,6 +133,7 @@
|
||||
@property (nonatomic, strong) UILabel *rankName_3;
|
||||
|
||||
@property (nonatomic, strong) UILabel *rankTipsLabel;
|
||||
@property (nonatomic, strong) UILabel *ratioLabel;
|
||||
|
||||
@end
|
||||
|
||||
@@ -438,6 +439,12 @@
|
||||
make.center.mas_equalTo(progressNumberBG);
|
||||
}];
|
||||
|
||||
[progressBG addSubview:self.ratioLabel];
|
||||
[self.ratioLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerX.mas_equalTo(progressBG).offset(2);
|
||||
make.centerY.mas_equalTo(progressBG);
|
||||
}];
|
||||
|
||||
UIImageView *rocketsBG = [self rocketsBG];
|
||||
[self.view addSubview:rocketsBG];
|
||||
[rocketsBG mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
@@ -681,6 +688,18 @@
|
||||
progress = 0.1;
|
||||
}
|
||||
|
||||
// 更新比值 label 并保持发光效果
|
||||
NSString *ratioText = [NSString stringWithFormat:@"%@/%@",
|
||||
@(boom.exp * boom.speed / 100.0).stringValue,
|
||||
@(boom.exp).stringValue];
|
||||
|
||||
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:ratioText];
|
||||
[attributedString addAttribute:NSStrokeColorAttributeName value:[UIColor whiteColor] range:NSMakeRange(0, attributedString.length)];
|
||||
[attributedString addAttribute:NSStrokeWidthAttributeName value:@(-2.0) range:NSMakeRange(0, attributedString.length)];
|
||||
[attributedString addAttribute:NSForegroundColorAttributeName value:[UIColor blackColor] range:NSMakeRange(0, attributedString.length)];
|
||||
[attributedString addAttribute:NSFontAttributeName value:kFontMedium(14) range:NSMakeRange(0, attributedString.length)];
|
||||
self.ratioLabel.attributedText = attributedString;
|
||||
|
||||
[UIView animateWithDuration:0.2 animations:^{
|
||||
[self.progressBar mas_updateConstraints:^(MASConstraintMaker *make) {
|
||||
make.height.mas_equalTo(kGetScaleWidth(180) * progress); // 动态调整高度
|
||||
@@ -1120,5 +1139,27 @@
|
||||
return _rankTipsLabel;
|
||||
}
|
||||
|
||||
- (UILabel *)ratioLabel {
|
||||
if (!_ratioLabel) {
|
||||
_ratioLabel = [UILabel labelInitWithText:@"0/0" font:kFontMedium(14) textColor:[UIColor blackColor]];
|
||||
_ratioLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_ratioLabel.transform = CGAffineTransformMakeRotation(M_PI_2); // 顺时针旋转90度
|
||||
|
||||
// 添加发光效果
|
||||
_ratioLabel.layer.shadowColor = [UIColor whiteColor].CGColor;
|
||||
_ratioLabel.layer.shadowOffset = CGSizeZero;
|
||||
_ratioLabel.layer.shadowRadius = 3.0;
|
||||
_ratioLabel.layer.shadowOpacity = 0.8;
|
||||
_ratioLabel.layer.masksToBounds = NO;
|
||||
|
||||
// 添加文字描边效果
|
||||
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@"0/0"];
|
||||
[attributedString addAttribute:NSStrokeColorAttributeName value:[UIColor whiteColor] range:NSMakeRange(0, attributedString.length)];
|
||||
[attributedString addAttribute:NSStrokeWidthAttributeName value:@(-2.0) range:NSMakeRange(0, attributedString.length)];
|
||||
_ratioLabel.attributedText = attributedString;
|
||||
}
|
||||
return _ratioLabel;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
20
YuMi/Modules/YMRoom/Model/MicCpInfoModel.h
Normal file
20
YuMi/Modules/YMRoom/Model/MicCpInfoModel.h
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// MicCpInfoModel.h
|
||||
// YuMi
|
||||
//
|
||||
// Created by P on 2025/9/8.
|
||||
//
|
||||
|
||||
#import "PIBaseModel.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MicCpInfoModel : PIBaseModel
|
||||
|
||||
@property (nonatomic, assign) NSInteger uid;
|
||||
@property (nonatomic, assign) NSInteger cpLevel;
|
||||
@property (nonatomic, assign) NSInteger loverUid;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
12
YuMi/Modules/YMRoom/Model/MicCpInfoModel.m
Normal file
12
YuMi/Modules/YMRoom/Model/MicCpInfoModel.m
Normal file
@@ -0,0 +1,12 @@
|
||||
//
|
||||
// MicCpInfoModel.m
|
||||
// YuMi
|
||||
//
|
||||
// Created by P on 2025/9/8.
|
||||
//
|
||||
|
||||
#import "MicCpInfoModel.h"
|
||||
|
||||
@implementation MicCpInfoModel
|
||||
|
||||
@end
|
@@ -82,6 +82,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (void)getShareLink:(NSString *)roomUid success:(void(^)(NSString *link))success failure:(void(^)(NSError *error))failure;
|
||||
|
||||
- (void)micCpListByRoomUid:(NSString *)roomUid;
|
||||
|
||||
- (void)micCpListByUidList:(NSArray<NSString *> *)uidList;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#import "XPRedPacketModel.h"
|
||||
#import "XPFreeGiftModel.h"
|
||||
#import "BoomInfoModel.h"
|
||||
#import "MicCpInfoModel.h"
|
||||
///P
|
||||
#import "XPRoomProtocol.h"
|
||||
|
||||
@@ -343,4 +344,29 @@
|
||||
} showLoading:YES errorToast:YES] targetUid:roomUid];
|
||||
}
|
||||
|
||||
- (void)micCpListByRoomUid:(NSString *)roomUid {
|
||||
@kWeakify(self);
|
||||
[Api getRoomMicCpListByRoomUid:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
|
||||
@kStrongify(self);
|
||||
if (code == 200) {
|
||||
if ([[self getView] respondsToSelector:@selector(getMicCpListByRoomUidSuccess:)]) {
|
||||
NSArray *cpList = [MicCpInfoModel modelsWithArray:data.data];
|
||||
[[self getView] getMicCpListByRoomUidSuccess:cpList];
|
||||
}
|
||||
}
|
||||
} roomUid:roomUid];
|
||||
}
|
||||
|
||||
- (void)micCpListByUidList:(NSArray<NSString *> *)uidList {
|
||||
NSString *uidsString = [uidList componentsJoinedByString:@","];
|
||||
[Api getRoomMicCpListByUidList:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
|
||||
if (code == 200) {
|
||||
if ([[self getView] respondsToSelector:@selector(getMicCpListByUidListSuccess:)]) {
|
||||
NSArray *cpList = [MicCpInfoModel modelsWithArray:data.data];
|
||||
[[self getView] getMicCpListByUidListSuccess:cpList];
|
||||
}
|
||||
}
|
||||
} uidList:uidsString];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@@ -10,7 +10,7 @@
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@class RoomInfoModel, UserInfoModel, NIMChatroom, FirstChargeRoomWindowModel, XPRedPacketModel, BoomInfoModel, BoomDetailModel;
|
||||
@class RoomInfoModel, UserInfoModel, NIMChatroom, FirstChargeRoomWindowModel, XPRedPacketModel, BoomInfoModel, BoomDetailModel, MicCpInfoModel;
|
||||
|
||||
@protocol XPRoomProtocol <NSObject>
|
||||
|
||||
@@ -42,7 +42,13 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
-(void)getKickUserListSuccessWithList:(NSArray *)list;
|
||||
|
||||
- (void)getRoomBoomInfoSuccess:(NSArray <BoomDetailModel*> *)models;
|
||||
|
||||
- (void)getRoomBoomExplosionSuccess:(BoomInfoModel *)model;
|
||||
|
||||
- (void)getMicCpListByRoomUidSuccess:(NSArray <MicCpInfoModel *> *)cpList;
|
||||
|
||||
- (void)getMicCpListByUidListSuccess:(NSArray <MicCpInfoModel *> *)cpList;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@@ -296,8 +296,6 @@ done:
|
||||
self.chatFaceTabList = [ChatFaceResponse modelsWithArray:data];
|
||||
NSString *cachePath = [self getFaceCachePath];
|
||||
|
||||
NSLog(@"-------------- 表情缓存路径: %@", cachePath);
|
||||
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
NSMutableDictionary *cachedUrls = [[defaults objectForKey:@"SVGACachedUrls"] mutableCopy] ?: [NSMutableDictionary dictionary];
|
||||
|
||||
@@ -310,7 +308,6 @@ done:
|
||||
// 检查URL是否已缓存且文件存在
|
||||
if ([cachedUrls[faceVo.faceUrl] isEqualToString:fileName] &&
|
||||
[[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
|
||||
NSLog(@"已有 SVGA文件缓存:%@", faceVo.faceUrl);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -320,7 +317,6 @@ done:
|
||||
cachedUrls[faceVo.faceUrl] = fileName;
|
||||
[defaults setObject:cachedUrls forKey:@"SVGACachedUrls"];
|
||||
[defaults synchronize];
|
||||
NSLog(@"SVGA文件缓存成功:%@ - %@", faceVo.faceUrl, filePath);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
@@ -5,11 +5,12 @@
|
||||
// Created by AI Assistant on 2024/12/19.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class MicMidpointRectManager;
|
||||
@class UIView;
|
||||
|
||||
@protocol MicMidpointRectManagerDelegate <NSObject>
|
||||
|
||||
@@ -23,6 +24,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@property (nonatomic, weak) id<MicMidpointRectManagerDelegate> delegate;
|
||||
@property (nonatomic, weak) UIView *containerView;
|
||||
/// 缓存:当前房间的CP关系列表
|
||||
@property (nonatomic, strong, readonly) NSArray *cachedCpList;
|
||||
/// 缓存:麦位position -> uid(无用户为-1)
|
||||
@property (nonatomic, strong, readonly) NSDictionary<NSNumber *, NSNumber *> *micPositionToUid;
|
||||
|
||||
/// 初始化方法
|
||||
- (instancetype)initWithContainerView:(UIView *)containerView;
|
||||
@@ -38,12 +43,59 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/// 移除指定位置的中点矩形
|
||||
- (void)removeMidpointRectAtFrame:(CGRect)frame;
|
||||
|
||||
/// 播放指定位置的SVGA动画
|
||||
/// 播放指定位置的随机SVGA动画
|
||||
- (void)playSVGAAnimationAtFrame:(CGRect)frame;
|
||||
|
||||
/// 播放指定位置的指定资源名SVGA动画(资源名不带后缀)
|
||||
- (void)playSVGAAnimationAtFrame:(CGRect)frame withNamed:(NSString *)resourceName;
|
||||
|
||||
/// 抽象:在指定中点位置绘制关系位(透明容器),并根据CP列表与两端uid匹配后播放对应等级SVGA
|
||||
/// - Parameters:
|
||||
/// - frame: 中点关系位frame
|
||||
/// - micPairText: 调试展示用的麦位对文本(仅DEBUG下显示,50%透明)
|
||||
/// - leftUid: 左侧麦位用户uid(0表示无)
|
||||
/// - rightUid: 右侧麦位用户uid(0表示无)
|
||||
/// - cpList: CP关系列表(MicCpInfoModel 数组)
|
||||
- (void)addRelationshipAtFrame:(CGRect)frame
|
||||
micPairText:(NSString *)micPairText
|
||||
leftUid:(NSInteger)leftUid
|
||||
rightUid:(NSInteger)rightUid
|
||||
cpList:(NSArray *)cpList;
|
||||
|
||||
/// 基于用户UID清理相关的中点矩形和SVGA动画
|
||||
/// - Parameter uids: 需要清理的用户UID数组
|
||||
- (void)removeMidpointRectsForUids:(NSArray<NSNumber *> *)uids;
|
||||
|
||||
/// 设置并缓存CP列表
|
||||
- (void)setCpListCache:(NSArray *)cpList;
|
||||
|
||||
/// 使用当前stageView重建麦位快照(position->uid),无用户记为-1
|
||||
- (void)rebuildMicSnapshotWithStageView:(id)stageView micCount:(NSInteger)micCount;
|
||||
|
||||
/// 基于上一次快照与当前stageView的实际用户,生成“变动用户+左右邻居”的uid列表(字符串数组)并更新快照
|
||||
- (NSArray<NSString *> *)uidListForChangedMicWithStageView:(id)stageView micCount:(NSInteger)micCount;
|
||||
|
||||
/// 计算本次与快照的差异,返回 @{ @"added": NSArray<NSNumber*>, @"removed": NSArray<NSNumber*> } 并更新快照
|
||||
- (NSDictionary<NSString *, NSArray<NSNumber *> *> *)diffMicChangeWithStageView:(id)stageView micCount:(NSInteger)micCount;
|
||||
|
||||
/// 从缓存中移除包含这些uid的所有CP关系
|
||||
- (void)removeCpEntriesForUids:(NSArray<NSNumber *> *)uids;
|
||||
|
||||
/// 用新列表替换指定uid相关的CP关系(先移除涉及这些uid的旧数据,再合并新数据)
|
||||
- (void)mergeCpListCache:(NSArray *)cpList replaceForUids:(NSArray<NSNumber *> *)uids;
|
||||
|
||||
/// 停止所有SVGA动画
|
||||
- (void)stopAllSVGAAnimations;
|
||||
|
||||
/// 处理下麦事件:移除相关用户的CP关系和SVGA显示
|
||||
/// - Parameters:
|
||||
/// - downMicUids: 下麦用户的UID数组
|
||||
/// - stageView: 当前舞台视图
|
||||
/// - micCount: 麦位总数
|
||||
- (void)handleDownMicEvent:(NSArray<NSNumber *> *)downMicUids
|
||||
stageView:(id)stageView
|
||||
micCount:(NSInteger)micCount;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@@ -6,13 +6,22 @@
|
||||
//
|
||||
|
||||
#import "MicMidpointRectManager.h"
|
||||
#import "MicCpInfoModel.h"
|
||||
#import <SVGA.h>
|
||||
#import "StageView.h"
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class SVGAImageView;
|
||||
@class SVGAParser;
|
||||
|
||||
@interface MicMidpointRectManager ()
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray<UIView *> *midpointRects;
|
||||
@property (nonatomic, strong) NSMutableDictionary<NSString *, SVGAImageView *> *svgaViews;
|
||||
@property (nonatomic, strong) SVGAParser *svgaParser;
|
||||
@property (nonatomic, strong) NSArray *cachedCpListInternal;
|
||||
@property (nonatomic, strong) NSMutableDictionary<NSNumber *, NSNumber *> *micPositionToUidInternal; // position->uid (-1 if empty)
|
||||
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSDictionary *> *midpointRectInfo; // frameKey -> @{@"leftUid": @(uid), @"rightUid": @(uid)}
|
||||
|
||||
@end
|
||||
|
||||
@@ -24,31 +33,39 @@
|
||||
_midpointRects = [NSMutableArray array];
|
||||
_svgaViews = [NSMutableDictionary dictionary];
|
||||
_svgaParser = [[SVGAParser alloc] init];
|
||||
_micPositionToUidInternal = [NSMutableDictionary dictionary];
|
||||
_midpointRectInfo = [NSMutableDictionary dictionary];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSArray *)cachedCpList {
|
||||
return self.cachedCpListInternal ?: @[];
|
||||
}
|
||||
|
||||
- (NSDictionary<NSNumber *,NSNumber *> *)micPositionToUid {
|
||||
return self.micPositionToUidInternal ?: @{};
|
||||
}
|
||||
|
||||
- (void)addMidpointRectAtFrame:(CGRect)frame
|
||||
micPairText:(NSString *)micPairText
|
||||
autoPlaySVGA:(BOOL)autoPlaySVGA {
|
||||
|
||||
// 创建背景矩形
|
||||
// 创建透明容器
|
||||
UIView *rectView = [[UIView alloc] initWithFrame:frame];
|
||||
rectView.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:0.3];
|
||||
rectView.layer.borderColor = [UIColor blueColor].CGColor;
|
||||
rectView.layer.borderWidth = 2.0;
|
||||
rectView.layer.cornerRadius = 8.0;
|
||||
rectView.backgroundColor = [UIColor clearColor];
|
||||
rectView.userInteractionEnabled = NO;
|
||||
rectView.tag = 56002;
|
||||
|
||||
// 添加标签显示麦位对
|
||||
#if DEBUG
|
||||
// 仅在DEBUG显示麦位对文本,50%透明
|
||||
UILabel *label = [[UILabel alloc] init];
|
||||
label.text = micPairText;
|
||||
label.textColor = [UIColor whiteColor];
|
||||
label.textColor = [[UIColor whiteColor] colorWithAlphaComponent:0.5];
|
||||
label.font = [UIFont boldSystemFontOfSize:12];
|
||||
label.textAlignment = NSTextAlignmentCenter;
|
||||
label.frame = rectView.bounds;
|
||||
[rectView addSubview:label];
|
||||
#endif
|
||||
|
||||
// 添加到容器视图
|
||||
[self.containerView addSubview:rectView];
|
||||
@@ -62,6 +79,156 @@
|
||||
NSLog(@"🔧 添加中点矩形: %@, frame: %@", micPairText, NSStringFromCGRect(frame));
|
||||
}
|
||||
|
||||
- (void)addRelationshipAtFrame:(CGRect)frame
|
||||
micPairText:(NSString *)micPairText
|
||||
leftUid:(NSInteger)leftUid
|
||||
rightUid:(NSInteger)rightUid
|
||||
cpList:(NSArray *)cpList {
|
||||
// 基础容器
|
||||
[self addMidpointRectAtFrame:frame micPairText:micPairText autoPlaySVGA:NO];
|
||||
|
||||
// 存储中点矩形的用户信息
|
||||
NSString *frameKey = NSStringFromCGRect(frame);
|
||||
self.midpointRectInfo[frameKey] = @{
|
||||
@"leftUid": @(leftUid),
|
||||
@"rightUid": @(rightUid)
|
||||
};
|
||||
|
||||
if (leftUid <= 0 || rightUid <= 0 || cpList.count == 0) {
|
||||
return;
|
||||
}
|
||||
// 遍历匹配并播放对应等级的SVGA
|
||||
for (MicCpInfoModel *obj in cpList) {
|
||||
BOOL match = (obj.uid == leftUid && obj.loverUid == rightUid) || (obj.uid == rightUid && obj.loverUid == leftUid);
|
||||
if (match) {
|
||||
NSInteger safeLevel = MAX(1, MIN(5, obj.cpLevel+1));
|
||||
NSString *svgaName = [NSString stringWithFormat:@"mic_cp_lv%ld", (long)safeLevel];
|
||||
[self playSVGAAnimationAtFrame:frame withNamed:svgaName];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Cache & Snapshot
|
||||
|
||||
- (void)setCpListCache:(NSArray *)cpList {
|
||||
self.cachedCpListInternal = cpList ?: @[];
|
||||
}
|
||||
|
||||
- (void)rebuildMicSnapshotWithStageView:(id)stageView micCount:(NSInteger)micCount {
|
||||
[self.micPositionToUidInternal removeAllObjects];
|
||||
for (NSInteger i = 0; i < micCount; i++) {
|
||||
NSInteger uid = -1;
|
||||
if ([stageView respondsToSelector:@selector(findMicroViewByIndex:)]) {
|
||||
UIView *microView = [(StageView *)stageView findMicroViewByIndex:i];
|
||||
if ([microView respondsToSelector:@selector(getUser)]) {
|
||||
id userInfo = [microView performSelector:@selector(getUser)];
|
||||
if (userInfo && [userInfo respondsToSelector:@selector(uid)]) {
|
||||
uid = (NSInteger)[userInfo valueForKey:@"uid"];
|
||||
if (uid <= 0) { uid = -1; }
|
||||
}
|
||||
}
|
||||
}
|
||||
self.micPositionToUidInternal[@(i)] = @(uid);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)uidListForChangedMicWithStageView:(id)stageView micCount:(NSInteger)micCount {
|
||||
NSMutableSet<NSNumber *> *result = [NSMutableSet set];
|
||||
// 计算新状态
|
||||
NSMutableArray<NSNumber *> *newUids = [NSMutableArray arrayWithCapacity:micCount];
|
||||
for (NSInteger i = 0; i < micCount; i++) {
|
||||
NSInteger uid = -1;
|
||||
if ([stageView respondsToSelector:@selector(findMicroViewByIndex:)]) {
|
||||
UIView *microView = [(StageView *)stageView findMicroViewByIndex:i];
|
||||
if ([microView respondsToSelector:@selector(getUser)]) {
|
||||
id userInfo = [microView performSelector:@selector(getUser)];
|
||||
if (userInfo && [userInfo respondsToSelector:@selector(uid)]) {
|
||||
uid = (NSInteger)[userInfo valueForKey:@"uid"];
|
||||
if (uid <= 0) { uid = -1; }
|
||||
}
|
||||
}
|
||||
}
|
||||
[newUids addObject:@(uid)];
|
||||
NSNumber *oldVal = self.micPositionToUidInternal[@(i)] ?: @(-1);
|
||||
if (oldVal.integerValue != uid) {
|
||||
// 该坑位发生变化,将该位及其左右邻居加入
|
||||
if (uid > 0) { [result addObject:@(uid)]; }
|
||||
NSInteger leftIdx = i - 1;
|
||||
NSInteger rightIdx = i + 1;
|
||||
if (leftIdx >= 0) {
|
||||
NSInteger leftUid = (leftIdx < newUids.count) ? newUids[leftIdx].integerValue : -1;
|
||||
if (leftUid > 0) { [result addObject:@(leftUid)]; }
|
||||
}
|
||||
if (rightIdx < micCount) {
|
||||
NSInteger rightUid = (rightIdx < newUids.count) ? newUids[rightIdx].integerValue : -1;
|
||||
if (rightUid > 0) { [result addObject:@(rightUid)]; }
|
||||
}
|
||||
}
|
||||
}
|
||||
// 更新快照
|
||||
for (NSInteger i = 0; i < micCount; i++) {
|
||||
self.micPositionToUidInternal[@(i)] = newUids[i];
|
||||
}
|
||||
// 转字符串数组
|
||||
NSMutableArray<NSString *> *uids = [NSMutableArray arrayWithCapacity:result.count];
|
||||
for (NSNumber *num in result) { [uids addObject:num.stringValue]; }
|
||||
return uids;
|
||||
}
|
||||
|
||||
- (NSDictionary<NSString *, NSArray<NSNumber *> *> *)diffMicChangeWithStageView:(id)stageView micCount:(NSInteger)micCount {
|
||||
NSMutableArray<NSNumber *> *added = [NSMutableArray array];
|
||||
NSMutableArray<NSNumber *> *removed = [NSMutableArray array];
|
||||
for (NSInteger i = 0; i < micCount; i++) {
|
||||
NSInteger uid = -1;
|
||||
if ([stageView respondsToSelector:@selector(findMicroViewByIndex:)]) {
|
||||
UIView *microView = [(StageView *)stageView findMicroViewByIndex:i];
|
||||
if ([microView respondsToSelector:@selector(getUser)]) {
|
||||
id userInfo = [microView performSelector:@selector(getUser)];
|
||||
if (userInfo && [userInfo respondsToSelector:@selector(uid)]) {
|
||||
uid = (NSInteger)[userInfo valueForKey:@"uid"];
|
||||
if (uid <= 0) { uid = -1; }
|
||||
}
|
||||
}
|
||||
}
|
||||
NSInteger oldUid = (self.micPositionToUidInternal[@(i)] ?: @(-1)).integerValue;
|
||||
if (uid != oldUid) {
|
||||
if (oldUid > 0 && uid == -1) { // 下麦
|
||||
[removed addObject:@(oldUid)];
|
||||
} else if (uid > 0) { // 上麦或换位到此
|
||||
[added addObject:@(uid)];
|
||||
}
|
||||
}
|
||||
self.micPositionToUidInternal[@(i)] = @(uid);
|
||||
}
|
||||
return @{ @"added": added.copy, @"removed": removed.copy };
|
||||
}
|
||||
|
||||
- (void)removeCpEntriesForUids:(NSArray<NSNumber *> *)uids {
|
||||
if (uids.count == 0 || self.cachedCpListInternal.count == 0) { return; }
|
||||
NSMutableArray *filtered = [NSMutableArray array];
|
||||
NSSet *uidSet = [NSSet setWithArray:uids];
|
||||
for (id obj in self.cachedCpListInternal) {
|
||||
NSInteger a = 0, b = 0;
|
||||
if ([obj respondsToSelector:@selector(uid)]) { a = (NSInteger)[obj valueForKey:@"uid"]; }
|
||||
if ([obj respondsToSelector:@selector(loverUid)]) { b = (NSInteger)[obj valueForKey:@"loverUid"]; }
|
||||
if (![uidSet containsObject:@(a)] && ![uidSet containsObject:@(b)]) {
|
||||
[filtered addObject:obj];
|
||||
}
|
||||
}
|
||||
self.cachedCpListInternal = filtered.copy;
|
||||
}
|
||||
|
||||
- (void)mergeCpListCache:(NSArray *)cpList replaceForUids:(NSArray<NSNumber *> *)uids {
|
||||
// 先移除相关uid的旧数据
|
||||
[self removeCpEntriesForUids:uids];
|
||||
if (cpList.count == 0) { return; }
|
||||
// 合并新数据
|
||||
NSMutableArray *merged = [self.cachedCpListInternal mutableCopy] ?: [NSMutableArray array];
|
||||
[merged addObjectsFromArray:cpList];
|
||||
self.cachedCpListInternal = merged.copy;
|
||||
}
|
||||
|
||||
- (void)removeAllMidpointRects {
|
||||
// 停止所有SVGA动画
|
||||
[self stopAllSVGAAnimations];
|
||||
@@ -71,6 +238,7 @@
|
||||
[rectView removeFromSuperview];
|
||||
}
|
||||
[self.midpointRects removeAllObjects];
|
||||
[self.midpointRectInfo removeAllObjects];
|
||||
|
||||
NSLog(@"🔧 移除所有中点矩形");
|
||||
}
|
||||
@@ -94,6 +262,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 移除对应的用户信息
|
||||
[self.midpointRectInfo removeObjectForKey:frameKey];
|
||||
|
||||
NSLog(@"🔧 移除指定位置的中点矩形: %@", NSStringFromCGRect(frame));
|
||||
}
|
||||
|
||||
@@ -101,40 +272,35 @@
|
||||
// 随机选择一个SVGA资源
|
||||
NSArray *svgaFiles = @[@"mic_cp_lv1", @"mic_cp_lv2", @"mic_cp_lv3", @"mic_cp_lv4", @"mic_cp_lv5"];
|
||||
NSString *randomSVGA = svgaFiles[arc4random_uniform((uint32_t)svgaFiles.count)];
|
||||
|
||||
// 构建SVGA文件路径
|
||||
NSString *svgaPath = [[NSBundle mainBundle] pathForResource:randomSVGA ofType:@"svga"];
|
||||
[self playSVGAAnimationAtFrame:frame withNamed:randomSVGA];
|
||||
}
|
||||
|
||||
// 播放指定资源名SVGA
|
||||
- (void)playSVGAAnimationAtFrame:(CGRect)frame withNamed:(NSString *)resourceName {
|
||||
if (resourceName.length == 0) { return; }
|
||||
NSString *svgaPath = [[NSBundle mainBundle] pathForResource:resourceName ofType:@"svga"];
|
||||
if (!svgaPath) {
|
||||
NSLog(@"⚠️ 找不到SVGA文件: %@", randomSVGA);
|
||||
NSLog(@"⚠️ 找不到SVGA文件: %@", resourceName);
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建SVGAImageView
|
||||
SVGAImageView *svgaView = [[SVGAImageView alloc] initWithFrame:frame];
|
||||
svgaView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
svgaView.userInteractionEnabled = NO;
|
||||
svgaView.backgroundColor = [UIColor clearColor];
|
||||
|
||||
// 添加到容器视图
|
||||
[self.containerView addSubview:svgaView];
|
||||
|
||||
// 保存SVGA视图引用
|
||||
NSString *frameKey = NSStringFromCGRect(frame);
|
||||
self.svgaViews[frameKey] = svgaView;
|
||||
|
||||
// 解析并播放SVGA动画
|
||||
[self.svgaParser parseWithURL:[NSURL fileURLWithPath:svgaPath]
|
||||
[self.svgaParser parseWithURL:[NSURL fileURLWithPath:svgaPath]
|
||||
completionBlock:^(SVGAVideoEntity * _Nonnull videoItem) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
svgaView.videoItem = videoItem;
|
||||
svgaView.loops = 0;
|
||||
svgaView.loops = 0;
|
||||
svgaView.clearsAfterStop = YES;
|
||||
[svgaView startAnimation];
|
||||
|
||||
NSLog(@"🎬 开始播放SVGA动画: %@, frame: %@", randomSVGA, NSStringFromCGRect(frame));
|
||||
NSLog(@"🎬 开始播放SVGA动画: %@, frame: %@", resourceName, NSStringFromCGRect(frame));
|
||||
});
|
||||
} failureBlock:^(NSError * _Nonnull error) {
|
||||
NSLog(@"❌ SVGA动画解析失败: %@, error: %@", randomSVGA, error.localizedDescription);
|
||||
NSLog(@"❌ SVGA动画解析失败: %@, error: %@", resourceName, error.localizedDescription);
|
||||
[svgaView removeFromSuperview];
|
||||
[self.svgaViews removeObjectForKey:frameKey];
|
||||
}];
|
||||
@@ -150,6 +316,60 @@
|
||||
NSLog(@"🔧 停止所有SVGA动画");
|
||||
}
|
||||
|
||||
- (void)removeMidpointRectsForUids:(NSArray<NSNumber *> *)uids {
|
||||
if (uids.count == 0) {
|
||||
NSLog(@"🔧 基于UID清理:没有需要清理的用户,跳过处理");
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"🔧 基于UID清理:开始清理用户 %@ 相关的中点矩形和SVGA", uids);
|
||||
|
||||
NSSet<NSNumber *> *uidSet = [NSSet setWithArray:uids];
|
||||
NSMutableArray<NSString *> *frameKeysToRemove = [NSMutableArray array];
|
||||
|
||||
// 遍历所有中点矩形信息,找出涉及下麦用户的矩形
|
||||
for (NSString *frameKey in self.midpointRectInfo.allKeys) {
|
||||
NSDictionary *rectInfo = self.midpointRectInfo[frameKey];
|
||||
NSNumber *leftUid = rectInfo[@"leftUid"];
|
||||
NSNumber *rightUid = rectInfo[@"rightUid"];
|
||||
|
||||
// 检查是否涉及下麦用户
|
||||
if ([uidSet containsObject:leftUid] || [uidSet containsObject:rightUid]) {
|
||||
[frameKeysToRemove addObject:frameKey];
|
||||
}
|
||||
}
|
||||
|
||||
// 移除相关的中点矩形和SVGA动画
|
||||
for (NSString *frameKey in frameKeysToRemove) {
|
||||
CGRect frame = CGRectFromString(frameKey);
|
||||
[self removeMidpointRectAtFrame:frame];
|
||||
}
|
||||
|
||||
NSLog(@"🔧 基于UID清理完成:移除了 %lu 个中点矩形和SVGA动画", (unsigned long)frameKeysToRemove.count);
|
||||
}
|
||||
|
||||
- (void)handleDownMicEvent:(NSArray<NSNumber *> *)downMicUids
|
||||
stageView:(id)stageView
|
||||
micCount:(NSInteger)micCount {
|
||||
if (downMicUids.count == 0) {
|
||||
NSLog(@"🔧 处理下麦事件:没有下麦用户,跳过处理");
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"🔧 处理下麦事件:下麦用户 %@", downMicUids);
|
||||
|
||||
// 1. 从缓存中移除相关用户的CP关系
|
||||
[self removeCpEntriesForUids:downMicUids];
|
||||
|
||||
// 2. 基于UID精确清理相关的中点矩形和SVGA动画
|
||||
[self removeMidpointRectsForUids:downMicUids];
|
||||
|
||||
// 3. 更新麦位快照
|
||||
[self rebuildMicSnapshotWithStageView:stageView micCount:micCount];
|
||||
|
||||
NSLog(@"🔧 下麦事件处理完成");
|
||||
}
|
||||
|
||||
#pragma mark - Dealloc
|
||||
|
||||
- (void)dealloc {
|
||||
|
@@ -677,7 +677,6 @@
|
||||
NSString * headWearUrl = userInfo.headwearEffect.length ? userInfo.headwearEffect : userInfo.headWearUrl.length ? userInfo.headWearUrl : userInfo.headwearPic;
|
||||
if (headWearUrl.length > 0 && !userInfo.vipMic) {
|
||||
if ([userInfo isHeadWearSVGA]) {
|
||||
NSLog(@"🎮 MicroView: 配置 SVGA 头饰: %@", headWearUrl);
|
||||
self.headWearSVGAImageView.hidden = NO;
|
||||
SVGAParser *parse = [[SVGAParser alloc] init];
|
||||
@kWeakify(self);
|
||||
@@ -690,22 +689,18 @@
|
||||
}
|
||||
} failureBlock:^(NSError * _Nullable error) { }];
|
||||
} else {
|
||||
NSLog(@"🎮 MicroView: 配置精灵图头饰: %@", headWearUrl);
|
||||
self.headWearImageView.hidden = NO;
|
||||
NSURL *url = [NSURL URLWithString:headWearUrl];
|
||||
@kWeakify(self);
|
||||
[self.manager loadSpriteSheetImageWithURL:url completionBlock:^(YYSpriteSheetImage * _Nullable sprit) {
|
||||
@kStrongify(self);
|
||||
self.headWearImageView.image = sprit;
|
||||
NSLog(@"🎮 MicroView: 精灵图头饰加载完成,应用 turbo mode 状态");
|
||||
// 加载完成后应用 turbo mode 状态
|
||||
[self applyTurboModeToHeadWear];
|
||||
} failureBlock:^(NSError * _Nullable error) {
|
||||
NSLog(@"🎮 MicroView: 精灵图头饰加载失败: %@", error.localizedDescription);
|
||||
}];
|
||||
}
|
||||
} else {
|
||||
NSLog(@"🎮 MicroView: 隐藏头饰 (URL为空或VIP麦位)");
|
||||
self.headWearSVGAImageView.hidden = YES;
|
||||
self.headWearImageView.hidden = YES;
|
||||
}
|
||||
@@ -827,23 +822,16 @@
|
||||
- (void)handleTurboModeStateChanged:(NSNotification *)notification {
|
||||
NSDictionary *userInfo = notification.userInfo;
|
||||
BOOL enabled = [userInfo[@"enabled"] boolValue];
|
||||
|
||||
NSLog(@"🎮 MicroView: 收到 turbo mode 状态变化通知,新状态: %@", enabled ? @"开启" : @"关闭");
|
||||
|
||||
|
||||
self.isTurboModeEnabled = enabled;
|
||||
|
||||
// 如果当前有头饰,需要重新配置以应用新的 turbo mode 状态
|
||||
if (self.userInfo && (self.userInfo.headwearEffect.length || self.userInfo.headWearUrl.length || self.userInfo.headwearPic.length)) {
|
||||
NSLog(@"🎮 MicroView: 当前有头饰,更新 turbo mode 状态");
|
||||
[self updateHeadWearForTurboMode];
|
||||
} else {
|
||||
NSLog(@"🎮 MicroView: 当前无头饰,跳过 turbo mode 状态更新");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateHeadWearForTurboMode {
|
||||
NSLog(@"🎮 MicroView: 更新头饰 turbo mode 状态,当前状态: %@", self.isTurboModeEnabled ? @"开启" : @"关闭");
|
||||
|
||||
if (self.isTurboModeEnabled) {
|
||||
// Turbo mode 开启:只显示第一帧,不播放动画
|
||||
[self setHeadWearToFirstFrameOnly];
|
||||
@@ -853,12 +841,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setHeadWearToFirstFrameOnly {
|
||||
NSLog(@"🎮 MicroView: 设置头饰为只显示第一帧模式");
|
||||
|
||||
- (void)setHeadWearToFirstFrameOnly {
|
||||
// 对于 YYAnimatedImageView,停止动画并显示第一帧
|
||||
if (!self.headWearImageView.hidden) {
|
||||
NSLog(@"🎮 MicroView: 停止 YYAnimatedImageView 动画");
|
||||
[self.headWearImageView stopAnimating];
|
||||
UIImage <YYAnimatedImage> *sprites = (YYSpriteSheetImage *)self.headWearImageView.image;
|
||||
self.tempSprites = sprites;
|
||||
@@ -866,36 +851,28 @@
|
||||
if (firstFrame) {
|
||||
self.headWearImageView.image = firstFrame;
|
||||
}
|
||||
} else
|
||||
|
||||
// 对于 SVGAImageView,停止动画
|
||||
if (!self.headWearSVGAImageView.hidden) {
|
||||
NSLog(@"🎮 MicroView: 停止 SVGAImageView 动画");
|
||||
} else if (!self.headWearSVGAImageView.hidden) {
|
||||
// 对于 SVGAImageView,停止动画
|
||||
[self.headWearSVGAImageView pauseAnimation];
|
||||
// [self.headWearSVGAImageView stepToFrame:1 andPlay:NO];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setHeadWearToNormalPlayback {
|
||||
NSLog(@"🎮 MicroView: 设置头饰为正常播放模式");
|
||||
|
||||
// 对于 YYAnimatedImageView,恢复动画播放
|
||||
if (!self.headWearImageView.hidden && self.headWearImageView.image) {
|
||||
NSLog(@"🎮 MicroView: 恢复 YYAnimatedImageView 动画播放");
|
||||
[self.headWearImageView startAnimating];
|
||||
}
|
||||
|
||||
// 对于 SVGAImageView,恢复动画播放
|
||||
if (!self.headWearSVGAImageView.hidden && self.headWearSVGAImageView.videoItem) {
|
||||
NSLog(@"🎮 MicroView: 恢复 SVGAImageView 动画播放");
|
||||
self.headWearImageView.image = self.tempSprites;
|
||||
[self.headWearSVGAImageView startAnimation];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applyTurboModeToHeadWear {
|
||||
NSLog(@"🎮 MicroView: 应用 turbo mode 到头饰,当前状态: %@", self.isTurboModeEnabled ? @"开启" : @"关闭");
|
||||
|
||||
if (self.isTurboModeEnabled) {
|
||||
// Turbo mode 开启:只显示第一帧,不播放动画
|
||||
[self setHeadWearToFirstFrameOnly];
|
||||
|
@@ -94,6 +94,10 @@
|
||||
#import "RoomResourceManager.h"
|
||||
#import "LuckyPackageLogicManager.h"
|
||||
|
||||
#import "MicCpInfoModel.h"
|
||||
#import "XPMessageRemoteExtModel.h"
|
||||
#import "MicMidpointRectManager.h"
|
||||
|
||||
// 🔧 新增:Turbo Mode Tips 相关
|
||||
#import "XPTurboModeTipsManager.h"
|
||||
#import "BuglyManager.h"
|
||||
@@ -194,6 +198,10 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
///是否正在显示红包弹窗,防止显示多个弹窗
|
||||
@property(nonatomic,assign) BOOL isShowRedPacket;
|
||||
@property(nonatomic,copy) NSString *releaseCoins;
|
||||
|
||||
// 🔧 防护机制:房间状态标志位
|
||||
@property (atomic, assign) BOOL isExitingRoom; // 是否正在退出房间
|
||||
@property (atomic, assign) BOOL isViewActive; // VC 是否可见/活跃
|
||||
@property(nonatomic,copy) NSString *myCoins;
|
||||
|
||||
@property(nonatomic,strong) UIButton *exitGameButton;
|
||||
@@ -204,6 +212,13 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
/// 🔧 修复:保存 block 形式的通知观察者,防止内存泄漏
|
||||
@property(nonatomic,strong) id<NSObject> exchangeRoomAnimationViewObserver;
|
||||
|
||||
/// 当前房间的CP关系列表(用于在中点关系位播放对应cp等级的SVGA)
|
||||
@property (nonatomic, strong) NSArray<MicCpInfoModel *> *currentCpList;
|
||||
@property (nonatomic, assign) BOOL currentUserMicStatusChanged; // 当前用户麦位状态是否发生变化(上麦、下麦、换位)
|
||||
@property (nonatomic, assign) BOOL currentUserWasOnMic; // 当前用户之前是否在麦上
|
||||
@property (nonatomic, assign) NSInteger currentUserMicPosition; // 当前用户的麦位位置(-1表示不在麦上)
|
||||
@property (nonatomic, assign) BOOL hasCompletedRoomInitialization; // 是否已完成进房初始化
|
||||
|
||||
@end
|
||||
|
||||
@implementation XPRoomViewController
|
||||
@@ -296,6 +311,13 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
|
||||
NSLog(@"🔄 XPRoomViewController: 开始销毁");
|
||||
|
||||
// 🔧 防护:强制清理 TRTC 连接,防止资源泄漏
|
||||
[[RtcManager instance] exitRoom];
|
||||
|
||||
// 🔧 防护:标记房间正在退出,防止后续异步回调
|
||||
self.isExitingRoom = YES;
|
||||
self.isViewActive = NO;
|
||||
|
||||
[[RoomBoomManager sharedManager] leaveRoom];
|
||||
|
||||
[XPSkillCardPlayerManager shareInstance].photoIdList = nil;
|
||||
@@ -552,6 +574,15 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
[super viewWillDisappear:animated];
|
||||
self.freeView.hidden = YES;
|
||||
|
||||
// 🔧 防护:标记 VC 即将不可见
|
||||
self.isViewActive = NO;
|
||||
|
||||
// 🔧 防护:如果 VC 被移除或 dismiss,强制清理 TRTC
|
||||
if (self.isMovingFromParentViewController || self.isBeingDismissed) {
|
||||
[[RtcManager instance] exitRoom];
|
||||
self.isExitingRoom = YES;
|
||||
}
|
||||
|
||||
// 如果连击正在进行,强制重置
|
||||
if ([[GiftComboManager sharedManager] isActive]) {
|
||||
NSLog(@"📱 房间即将退出,检查连击状态");
|
||||
@@ -564,6 +595,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
|
||||
[XPSkillCardPlayerManager shareInstance].isInRoomVC = NO;
|
||||
|
||||
// 🔧 防护:确保标记为不可见
|
||||
self.isViewActive = NO;
|
||||
|
||||
// 🔧 修复:发送房间退出通知,让 BuglyManager 知道用户已退出房间
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"RoomDidExit"
|
||||
object:nil
|
||||
@@ -576,6 +610,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
|
||||
[XPSkillCardPlayerManager shareInstance].isInRoomVC = YES;
|
||||
|
||||
// 🔧 防护:标记 VC 可见
|
||||
self.isViewActive = YES;
|
||||
|
||||
// 🔧 修复:发送房间进入通知,让 BuglyManager 知道用户已进入房间
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"RoomDidEnter"
|
||||
object:nil
|
||||
@@ -634,7 +671,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
[self.view addSubview:self.animationView];
|
||||
|
||||
// 🔧 新增:添加测试按钮,用于测试 Turbo Mode Tips 弹窗
|
||||
#if DEBUG
|
||||
[self addTestButton];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)__layoutTwentyMicStage {
|
||||
@@ -1029,6 +1068,13 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
|
||||
// 🔧 新增:如果是 SocialStageView,绘制中点矩形
|
||||
[self drawSocialStageMidpointRects];
|
||||
|
||||
// 🔧 新增:进房成功后调用CP相关API
|
||||
[self callMicCpListByRoomUidAfterRoomEntered];
|
||||
|
||||
// 🔧 新增:stage view类型变化时调用CP相关API
|
||||
NSMutableDictionary<NSString *,MicroQueueModel *> *currentQueue = [self.stageView getMicroQueue];
|
||||
[self callMicCpListByUidListOnMicChangeWithQueue:currentQueue];
|
||||
|
||||
[self addExitGameButton];
|
||||
|
||||
@@ -1362,7 +1408,10 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
[self.backContainerView onRoomEntered];
|
||||
[self.littleGameView onRoomEntered];
|
||||
if ([XPRoomMiniManager shareManager].getRoomInfo.uid != self.roomUid.integerValue) {// 最小化进房 还是原来的房间的话 不需要重新进入云信 因为压根没退
|
||||
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
|
||||
// 🔧 防护:检查是否允许进房
|
||||
if (!self.isExitingRoom && self.isViewActive) {
|
||||
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
|
||||
}
|
||||
[self.functionView onRoomEntered];
|
||||
[self.messageContainerView onRoomEntered];
|
||||
[self.menuContainerView onRoomEntered];
|
||||
@@ -1437,7 +1486,10 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
[self.backContainerView onRoomEntered];
|
||||
[self.littleGameView onRoomEntered];
|
||||
if ([XPRoomMiniManager shareManager].getRoomInfo.uid != self.roomUid.integerValue) {// 最小化进房 还是原来的房间的话 不需要重新进入云信 因为压根没退
|
||||
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
|
||||
// 🔧 防护:检查是否允许进房
|
||||
if (!self.isExitingRoom && self.isViewActive) {
|
||||
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
|
||||
}
|
||||
[self.functionView onRoomEntered];
|
||||
[self.messageContainerView onRoomEntered];
|
||||
[self.menuContainerView onRoomEntered];
|
||||
@@ -1661,15 +1713,27 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
|
||||
self.quickMessageContainerView.titleArray = self.roomInfo.speakTemplate;
|
||||
|
||||
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
|
||||
// 🔧 防护:检查是否允许进房
|
||||
if (!self.isExitingRoom && self.isViewActive) {
|
||||
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)enterRoomSuccess:(NIMChatroom *)chatRoom {
|
||||
[XNDJTDDLoadingTool hideHUDInView:self.navigationController.view];
|
||||
|
||||
// 🔧 防护:检查 VC 是否仍然有效
|
||||
if (self.isExitingRoom || !self.isViewActive) {
|
||||
NSLog(@"🔧 enterRoomSuccess: VC 已退出或不可见,忽略回调");
|
||||
return;
|
||||
}
|
||||
|
||||
[self.stageView onRoomEntered];
|
||||
[self.functionView onRoomEntered];
|
||||
|
||||
// 🔧 新增:初始化当前用户的麦位状态
|
||||
[self initializeCurrentUserMicStatus];
|
||||
|
||||
//上报进房
|
||||
if (self.roomInfo != nil) {
|
||||
[self.presenter reportUserInterRoom:[NSString stringWithFormat:@"%zd", self.roomInfo.uid]];
|
||||
@@ -1685,6 +1749,13 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
- (void)enterRoomFail:(NSInteger)code {
|
||||
[XNDJTDDLoadingTool hideHUDInView:self.navigationController.view];
|
||||
[self hideHUD];
|
||||
|
||||
// 🔧 防护:检查 VC 是否仍然有效
|
||||
if (self.isExitingRoom || !self.isViewActive) {
|
||||
NSLog(@"🔧 enterRoomFail: VC 已退出或不可见,忽略回调 (code: %ld)", (long)code);
|
||||
return;
|
||||
}
|
||||
|
||||
if (code == 13003) {
|
||||
[self showErrorToast:YMLocalizedString(@"XPRoomViewController3")];
|
||||
}
|
||||
@@ -1851,7 +1922,10 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
[self.backContainerView onRoomEntered];
|
||||
[self.littleGameView onRoomEntered];
|
||||
if ([XPRoomMiniManager shareManager].getRoomInfo.uid != self.roomUid.integerValue) {// 最小化进房 还是原来的房间的话 不需要重新进入云信 因为压根没退
|
||||
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
|
||||
// 🔧 防护:检查是否允许进房
|
||||
if (!self.isExitingRoom && self.isViewActive) {
|
||||
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
|
||||
}
|
||||
[self.functionView onRoomEntered];
|
||||
[self.messageContainerView onRoomEntered];
|
||||
[self.menuContainerView onRoomEntered];
|
||||
@@ -1882,7 +1956,6 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
[self.presenter openRoom:title type:type roomPwd:@"" roomDesc:@"" backPic:@"" mgId:self.mgId];
|
||||
}
|
||||
} else { // 当前用户进入别人房间
|
||||
// TODO: 房主已经下线。
|
||||
[self showSuccessToast:YMLocalizedString(@"XPRoomViewController7")];
|
||||
[self enterRoomFail:0];
|
||||
}
|
||||
@@ -1994,21 +2067,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
}
|
||||
|
||||
if (![message.session.sessionId isEqualToString:@(self.roomInfo.roomId).stringValue]) {
|
||||
// if (message.messageType == NIMMessageTypeCustom) {
|
||||
// NIMCustomObject *obj = (NIMCustomObject *)message.messageObject;
|
||||
// if ([obj.attachment isKindOfClass:[AttachmentModel class]]) {
|
||||
// AttachmentModel *att = (AttachmentModel *)obj.attachment;
|
||||
// if (!att.isFromPublic) {
|
||||
// NSLog(@"[Recv] ⛔️ 过滤:房间不匹配 | msg.sid=%@ | curRoomId=%@",
|
||||
// message.session.sessionId, @(self.roomInfo.roomId).stringValue);
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
// }else {
|
||||
NSLog(@"[Recv] ⛔️ 过滤:房间不匹配 | msg.sid=%@ | curRoomId=%@",
|
||||
message.session.sessionId, @(self.roomInfo.roomId).stringValue);
|
||||
continue;
|
||||
// }
|
||||
NSLog(@"[Recv] ⛔️ 过滤:房间不匹配 | msg.sid=%@ | curRoomId=%@",
|
||||
message.session.sessionId, @(self.roomInfo.roomId).stringValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
NSLog(@"[Recv] --- Message Raw Attach Content: %@, %@, %ld", @(message.senderClientType), message.rawAttachContent, (long)message.messageType);
|
||||
@@ -2021,20 +2082,6 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
if (message.messageType == NIMMessageTypeNotification) {
|
||||
[self handleNIMNotificationTypeMessage:message];
|
||||
} else if (message.messageType == NIMMessageTypeCustom) {
|
||||
// 自定义消息排查日志:first/second/size3
|
||||
#if DEBUG
|
||||
if ([message.messageObject isKindOfClass:[NIMCustomObject class]]) {
|
||||
NIMCustomObject *obj = (NIMCustomObject *)message.messageObject;
|
||||
if ([obj.attachment isKindOfClass:[AttachmentModel class]]) {
|
||||
AttachmentModel *att = (AttachmentModel *)obj.attachment;
|
||||
NSData *payloadJSON = nil;
|
||||
@try { payloadJSON = [NSJSONSerialization dataWithJSONObject:att.data ?: @{} options:0 error:nil]; } @catch (__unused NSException *e) {}
|
||||
NSLog(@"[Recv] 🎯 自定义消息 | first=%ld second=%ld | payload=%lub | sid=%@ | ts=%.3f",
|
||||
(long)att.first, (long)att.second, (unsigned long)payloadJSON.length,
|
||||
message.session.sessionId, [[NSDate date] timeIntervalSince1970]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
[self handleNimCustomTypeMessage:message];
|
||||
} else if(message.messageType == NIMMessageTypeText) {
|
||||
[self.messageContainerView handleNIMTextMessage:message];
|
||||
@@ -2158,7 +2205,6 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
//房间类型是否变更了(从个播->普通,个播->小游戏等)
|
||||
newRoomInfo.hadChangeRoomType = self.roomInfo.type != newRoomInfo.type;
|
||||
BOOL anchorToOther = newRoomInfo.type != RoomType_Anchor && self.roomInfo.type == RoomType_Anchor;//个播变其他房
|
||||
RoomType currentType = self.roomInfo.type;
|
||||
self.roomInfo = newRoomInfo;
|
||||
|
||||
[self.backContainerView onRoomUpdate];
|
||||
@@ -2424,10 +2470,78 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case MicRelationship_Type:
|
||||
switch (attachment.second) {
|
||||
case MicRelationship_CP:
|
||||
[self handleMicRelationshipCPMessage:attachment];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理麦位关系CP消息
|
||||
- (void)handleMicRelationshipCPMessage:(AttachmentModel *)attachment {
|
||||
NSLog(@"🔧 接收到麦位关系CP消息");
|
||||
|
||||
if (!attachment.data || ![attachment.data isKindOfClass:[NSString class]]) {
|
||||
NSLog(@"⚠️ 麦位关系CP消息:data格式错误,跳过处理");
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *jsonString = (NSString *)attachment.data;
|
||||
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSError *error = nil;
|
||||
NSDictionary *jsonDict = [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]]) {
|
||||
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) {
|
||||
if ([cpDict isKindOfClass:[NSDictionary class]]) {
|
||||
MicCpInfoModel *cpInfo = [[MicCpInfoModel alloc] init];
|
||||
cpInfo.uid = [cpDict[@"uid"] integerValue];
|
||||
cpInfo.loverUid = [cpDict[@"loverUid"] integerValue];
|
||||
cpInfo.cpLevel = [cpDict[@"cpLevel"] integerValue];
|
||||
[cpList addObject:cpInfo];
|
||||
}
|
||||
}
|
||||
|
||||
NSLog(@"🔧 麦位关系CP消息:解析到 %lu 条CP数据", (unsigned long)cpList.count);
|
||||
|
||||
// 更新当前CP列表
|
||||
self.currentCpList = cpList.copy;
|
||||
|
||||
// 更新 MicMidpointRectManager 的缓存
|
||||
[self updateMicMidpointRectManagerCache:cpList];
|
||||
|
||||
// 刷新CP SVGA显示
|
||||
[self drawSocialStageMidpointRects];
|
||||
|
||||
NSLog(@"🔧 麦位关系CP消息处理完成");
|
||||
}
|
||||
|
||||
- (void)handleUpMicAsk:(AttachmentModel *)attachment {
|
||||
NSNumber *targetUid = [attachment.data objectForKey:@"targetUid"];
|
||||
if (!targetUid || ![targetUid.stringValue isEqualToString:[AccountInfoStorage instance].getUid]) {
|
||||
@@ -2731,6 +2845,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
- (void)exitRoom {
|
||||
[XNDJTDDLoadingTool showLoading];
|
||||
|
||||
// 🔧 防护:标记正在退出房间
|
||||
self.isExitingRoom = YES;
|
||||
|
||||
[XPSkillCardPlayerManager shareInstance].micState = MICState_None;
|
||||
|
||||
[self.stageView exitNIMRoom];
|
||||
@@ -2849,7 +2966,14 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
[self.littleGameView destroyMG];
|
||||
}
|
||||
[[XPRoomMiniManager shareManager] resetLocalMessage];
|
||||
|
||||
// 🔧 防护:确保 TRTC 退出
|
||||
[[RtcManager instance] exitRoom];
|
||||
|
||||
// 🔧 防护:标记房间已退出
|
||||
self.isExitingRoom = YES;
|
||||
self.isViewActive = NO;
|
||||
|
||||
[self.presenter exitNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId]];
|
||||
[self.presenter reportUserOutRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.uid]];
|
||||
[self handleFirstOutRoom];
|
||||
@@ -2946,19 +3070,31 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
[self.functionView onRoomUpdate];
|
||||
[self.functionView onMicroQueueUpdate:queue];
|
||||
|
||||
BOOL isOnMic = false;
|
||||
for (MicroQueueModel * info in queue.allValues) {
|
||||
if (info.userInfo.uid > 0 && [AccountInfoStorage instance].getUid.integerValue == info.userInfo.uid) {
|
||||
isOnMic = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 更新当前用户的麦位状态
|
||||
[self updateCurrentUserMicStatus:queue];
|
||||
|
||||
// 获取当前用户麦位状态
|
||||
NSDictionary *currentStatus = [self getCurrentUserMicStatus:queue];
|
||||
BOOL isOnMic = [currentStatus[@"isOnMic"] boolValue];
|
||||
|
||||
if (isOnMic) {
|
||||
self.anchorScrollView.scrollEnabled = NO;
|
||||
} else {
|
||||
self.anchorScrollView.scrollEnabled = YES;
|
||||
}
|
||||
|
||||
// 🔧 新增:只有在完成进房初始化后才调用mic位变动API
|
||||
if (self.hasCompletedRoomInitialization) {
|
||||
// 检测下麦用户并处理CP关系缓存
|
||||
[self handleDownMicEventIfNeeded:queue];
|
||||
|
||||
// 🔧 :更新麦位快照
|
||||
[self updateMicMidpointRectManagerSnapshot];
|
||||
|
||||
[self callMicCpListByUidListOnMicChangeWithQueue:queue];
|
||||
} else {
|
||||
NSLog(@"🔧 进房初始化中,跳过 micCpListByUidList 调用");
|
||||
}
|
||||
}
|
||||
|
||||
- (CGPoint)animationPointAtStageViewByUid:(NSString *)uid {
|
||||
@@ -2978,6 +3114,28 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
[[RoomBoomManager sharedManager] receiveEnterRoomBoom:model];
|
||||
}
|
||||
|
||||
- (void)getMicCpListByRoomUidSuccess:(NSArray <MicCpInfoModel *> *)cpList {
|
||||
self.currentCpList = cpList;
|
||||
// 刷新绘制,按CP关系播放对应SVGA
|
||||
[self drawSocialStageMidpointRects];
|
||||
}
|
||||
|
||||
- (void)getMicCpListByUidListSuccess:(NSArray<MicCpInfoModel *> *)cpList {
|
||||
self.currentCpList = cpList;
|
||||
|
||||
// 🔧 :更新 MicMidpointRectManager 的缓存
|
||||
[self updateMicMidpointRectManagerCache:cpList];
|
||||
|
||||
// 刷新绘制,按CP关系播放对应SVGA
|
||||
[self drawSocialStageMidpointRects];
|
||||
|
||||
// 如果当前用户麦位状态发生变化(上麦、下麦、换位),发送 NIM message
|
||||
if (self.currentUserMicStatusChanged) {
|
||||
[self sendMicRelationshipNIMessage:cpList];
|
||||
self.currentUserMicStatusChanged = NO; // 重置标志
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 首次退出非自己的房间,处理是否需要弹新用户充值优惠
|
||||
- (void)handleFirstOutRoom {
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
@@ -3056,7 +3214,10 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
[self.littleGameView onRoomEntered];
|
||||
[self.littleGameView onRoomEntered];
|
||||
if ([XPRoomMiniManager shareManager].getRoomInfo.uid != self.roomUid.integerValue) {// 最小化进房 还是原来的房间的话 不需要重新进入云信 因为压根没退
|
||||
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
|
||||
// 🔧 防护:检查是否允许进房
|
||||
if (!self.isExitingRoom && self.isViewActive) {
|
||||
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
|
||||
}
|
||||
[self.functionView onRoomEntered];
|
||||
[self.messageContainerView onRoomEntered];
|
||||
[self.menuContainerView onRoomEntered];
|
||||
@@ -3080,7 +3241,10 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
[self.backContainerView onRoomEntered];
|
||||
[self.littleGameView onRoomEntered];
|
||||
[self.functionView onRoomEntered];
|
||||
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
|
||||
// 🔧 防护:检查是否允许进房
|
||||
if (!self.isExitingRoom && self.isViewActive) {
|
||||
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
|
||||
}
|
||||
[self.messageContainerView onRoomEntered];
|
||||
[self.littleGameView onRoomEntered];
|
||||
[[XPRoomMiniManager shareManager] configRoomInfo:nil];
|
||||
@@ -3320,13 +3484,6 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用现有的消息处理流程
|
||||
// [self.messageContainerView handleNIMCustomMessage:message];
|
||||
// [self.animationView handleNIMCustomMessage:message];
|
||||
// [self handleNimCustomTypeMessage:message];
|
||||
// [self onRecvMessages:@[message]];
|
||||
|
||||
|
||||
switch (message.messageType) {
|
||||
case NIMMessageTypeNotification:
|
||||
[self handleNIMNotificationTypeMessage:message];
|
||||
@@ -3416,7 +3573,7 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
NSLog(@"🎮 卡顿检测模拟已触发,计数将增加");
|
||||
}
|
||||
|
||||
/// 调试方法:在所有 StageView 上绘制符合条件的相邻麦位中点矩形
|
||||
/// 在 StageView 上绘制符合条件的相邻麦位中点矩形
|
||||
- (void)drawSocialStageMidpointRects {
|
||||
if (!self.stageView) {
|
||||
NSLog(@"🔧 当前没有 stageView,跳过中点矩形绘制");
|
||||
@@ -3485,13 +3642,13 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
|
||||
CGRect rect = [self.stageView rectForMidpointBetweenMicAtIndex:firstIndex andIndex:secondIndex];
|
||||
if (!CGRectIsEmpty(rect)) {
|
||||
UIView *debugView = [[UIView alloc] initWithFrame:rect];
|
||||
debugView.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:0.3];
|
||||
debugView.layer.borderColor = [UIColor blueColor].CGColor;
|
||||
debugView.layer.borderWidth = 2.0;
|
||||
debugView.layer.cornerRadius = 8.0;
|
||||
debugView.tag = 56002;
|
||||
debugView.userInteractionEnabled = NO;
|
||||
UIView *micRelationshipView = [[UIView alloc] initWithFrame:rect];
|
||||
micRelationshipView.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:0.3];
|
||||
micRelationshipView.layer.borderColor = [UIColor blueColor].CGColor;
|
||||
micRelationshipView.layer.borderWidth = 2.0;
|
||||
micRelationshipView.layer.cornerRadius = 8.0;
|
||||
micRelationshipView.tag = 56002;
|
||||
micRelationshipView.userInteractionEnabled = NO;
|
||||
|
||||
// 添加标签显示麦位对
|
||||
UILabel *label = [[UILabel alloc] init];
|
||||
@@ -3499,23 +3656,22 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
label.textColor = [UIColor whiteColor];
|
||||
label.font = [UIFont boldSystemFontOfSize:12];
|
||||
label.textAlignment = NSTextAlignmentCenter;
|
||||
label.frame = debugView.bounds;
|
||||
[debugView addSubview:label];
|
||||
label.frame = micRelationshipView.bounds;
|
||||
[micRelationshipView addSubview:label];
|
||||
|
||||
// 根据不同的 StageView 类型添加到相应的容器
|
||||
if ([self.stageView isKindOfClass:[LittleGameScrollStageView class]]) {
|
||||
[(LittleGameScrollStageView *)self.stageView addMidpointRect:debugView];
|
||||
} else if ([self.stageView isKindOfClass:[LittleGameStageView class]]) {
|
||||
[(LittleGameStageView *)self.stageView addMidpointRect:debugView];
|
||||
} else if ([self.stageView respondsToSelector:@selector(midpointRectManager)]) {
|
||||
// 使用新的管理器添加中点矩形
|
||||
// 统一逻辑:优先通过 midpointRectManager 管理器添加与播放SVGA;无管理器时回退为直接添加
|
||||
if ([self.stageView respondsToSelector:@selector(midpointRectManager)]) {
|
||||
id manager = [self.stageView valueForKey:@"midpointRectManager"];
|
||||
if ([manager respondsToSelector:@selector(addMidpointRectAtFrame:micPairText:autoPlaySVGA:)]) {
|
||||
if ([manager respondsToSelector:@selector(addRelationshipAtFrame:micPairText:leftUid:rightUid:cpList:)]) {
|
||||
NSString *micPairText = [NSString stringWithFormat:@"%ld-%ld", (long)firstIndex, (long)secondIndex];
|
||||
[manager addMidpointRectAtFrame:rect micPairText:micPairText autoPlaySVGA:YES];
|
||||
UIView<MicroViewProtocol> *leftView = [self.stageView findMicroViewByIndex:firstIndex];
|
||||
UIView<MicroViewProtocol> *rightView = [self.stageView findMicroViewByIndex:secondIndex];
|
||||
NSInteger leftUid = [[leftView getUser] uid];
|
||||
NSInteger rightUid = [[rightView getUser] uid];
|
||||
[manager addRelationshipAtFrame:rect micPairText:micPairText leftUid:leftUid rightUid:rightUid cpList:self.currentCpList ?: @[]];
|
||||
}
|
||||
} else {
|
||||
[self.stageView addSubview:debugView];
|
||||
[self.stageView addSubview:micRelationshipView];
|
||||
}
|
||||
NSLog(@"🔧 绘制中点矩形: %ld-%ld, rect: %@", (long)firstIndex, (long)secondIndex, NSStringFromCGRect(rect));
|
||||
} else {
|
||||
@@ -3526,4 +3682,387 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
NSLog(@"🔧 %@ 中点矩形绘制完成", stageViewClass);
|
||||
}
|
||||
|
||||
/// 进房成功后调用CP相关API
|
||||
- (void)callMicCpListByRoomUidAfterRoomEntered {
|
||||
NSLog(@"🔧 进房成功:开始调用 micCpListByRoomUid API");
|
||||
|
||||
if (!self.roomInfo) {
|
||||
NSLog(@"⚠️ 进房成功:roomInfo为空,跳过CP API调用");
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *roomUid = [NSString stringWithFormat:@"%ld", (long)self.roomInfo.uid];
|
||||
NSLog(@"🔧 进房成功:房间UID: %@, 房间类型: %ld", roomUid, (long)self.roomInfo.type);
|
||||
|
||||
// 调用 micCpListByRoomUid 方法
|
||||
if ([self.presenter respondsToSelector:@selector(micCpListByRoomUid:)]) {
|
||||
NSLog(@"🔧 进房成功:调用 micCpListByRoomUid: %@", roomUid);
|
||||
[self.presenter micCpListByRoomUid:roomUid];
|
||||
} else {
|
||||
NSLog(@"⚠️ 进房成功:presenter不支持micCpListByRoomUid方法");
|
||||
}
|
||||
|
||||
NSLog(@"🔧 进房成功:micCpListByRoomUid API调用完成");
|
||||
}
|
||||
|
||||
/// 获取当前用户及其左右相邻用户的uid列表(最多3个)
|
||||
- (NSArray<NSString *> *)getAllMicUserUidsWithQueue:(NSMutableDictionary<NSString *,MicroQueueModel *> *)queue {
|
||||
NSMutableArray<NSString *> *micUserUids = [NSMutableArray array];
|
||||
|
||||
if (!self.roomInfo) {
|
||||
NSLog(@"⚠️ 获取mic用户列表:roomInfo为空");
|
||||
return micUserUids;
|
||||
}
|
||||
|
||||
// 获取当前用户的麦位位置
|
||||
NSInteger currentUserMicPosition = self.currentUserMicPosition;
|
||||
if (currentUserMicPosition == -1) {
|
||||
NSLog(@"🔧 获取mic用户列表:当前用户不在麦上,返回空数据");
|
||||
return micUserUids;
|
||||
}
|
||||
|
||||
// 获取最大麦位数
|
||||
NSInteger maxMicCount = 0;
|
||||
switch (self.roomInfo.type) {
|
||||
case RoomType_Game:
|
||||
maxMicCount = 9;
|
||||
break;
|
||||
case RoomType_10Mic:
|
||||
maxMicCount = 10;
|
||||
break;
|
||||
case RoomType_15Mic:
|
||||
maxMicCount = 15;
|
||||
break;
|
||||
case RoomType_19Mic:
|
||||
maxMicCount = 19;
|
||||
break;
|
||||
case RoomType_20Mic:
|
||||
maxMicCount = 20;
|
||||
break;
|
||||
default:
|
||||
maxMicCount = 9; // 默认9麦
|
||||
break;
|
||||
}
|
||||
|
||||
// 获取当前用户及其左右相邻用户的UID
|
||||
NSInteger leftPosition = currentUserMicPosition - 1;
|
||||
NSInteger rightPosition = currentUserMicPosition + 1;
|
||||
|
||||
// 获取左边用户
|
||||
if (leftPosition >= 0) {
|
||||
NSString *leftPositionKey = [NSString stringWithFormat:@"%ld", (long)leftPosition];
|
||||
MicroQueueModel *leftMicModel = queue[leftPositionKey];
|
||||
if (leftMicModel && leftMicModel.userInfo && leftMicModel.userInfo.uid > 0) {
|
||||
[micUserUids addObject:[NSString stringWithFormat:@"%ld", (long)leftMicModel.userInfo.uid]];
|
||||
}
|
||||
}
|
||||
|
||||
// 获取当前用户
|
||||
NSString *currentPositionKey = [NSString stringWithFormat:@"%ld", (long)currentUserMicPosition];
|
||||
MicroQueueModel *currentMicModel = queue[currentPositionKey];
|
||||
if (currentMicModel && currentMicModel.userInfo && currentMicModel.userInfo.uid > 0) {
|
||||
[micUserUids addObject:[NSString stringWithFormat:@"%ld", (long)currentMicModel.userInfo.uid]];
|
||||
}
|
||||
|
||||
// 获取右边用户
|
||||
if (rightPosition < maxMicCount) {
|
||||
NSString *rightPositionKey = [NSString stringWithFormat:@"%ld", (long)rightPosition];
|
||||
MicroQueueModel *rightMicModel = queue[rightPositionKey];
|
||||
if (rightMicModel && rightMicModel.userInfo && rightMicModel.userInfo.uid > 0) {
|
||||
[micUserUids addObject:[NSString stringWithFormat:@"%ld", (long)rightMicModel.userInfo.uid]];
|
||||
}
|
||||
}
|
||||
|
||||
// 如果只有当前用户一个人,返回空数据
|
||||
if (micUserUids.count == 1) {
|
||||
NSLog(@"🔧 获取mic用户列表:只有当前用户一个人,返回空数据");
|
||||
return [NSArray array];
|
||||
}
|
||||
|
||||
NSLog(@"🔧 获取mic用户列表:当前用户麦位 %ld,找到 %lu 个相关用户: %@",
|
||||
(long)currentUserMicPosition, (unsigned long)micUserUids.count, micUserUids);
|
||||
return micUserUids;
|
||||
}
|
||||
|
||||
/// 检测并处理下麦事件
|
||||
- (void)handleDownMicEventIfNeeded:(NSMutableDictionary<NSString *,MicroQueueModel *> *)queue {
|
||||
if (!self.stageView || ![self.stageView respondsToSelector:@selector(midpointRectManager)]) {
|
||||
NSLog(@"🔧 处理下麦事件:stageView 不支持 midpointRectManager,跳过处理");
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取当前舞台类型的麦位总数
|
||||
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 检测麦位变化
|
||||
// MicMidpointRectManager *midpointRectManager = [self.stageView midpointRectManager];
|
||||
MicMidpointRectManager *midpointRectManager = (MicMidpointRectManager *)[self.stageView performSelector:@selector(midpointRectManager)];
|
||||
if (midpointRectManager) {
|
||||
NSDictionary<NSString *, NSArray<NSNumber *> *> *micChanges =
|
||||
[midpointRectManager diffMicChangeWithStageView:self.stageView micCount:micCount];
|
||||
|
||||
NSArray<NSNumber *> *removedUids = micChanges[@"removed"];
|
||||
if (removedUids && removedUids.count > 0) {
|
||||
NSLog(@"🔧 检测到下麦用户:%@", removedUids);
|
||||
|
||||
// 调用 MicMidpointRectManager 处理下麦事件
|
||||
[midpointRectManager handleDownMicEvent:removedUids stageView:self.stageView micCount:micCount];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// mic位变动时调用CP相关API
|
||||
- (void)callMicCpListByUidListOnMicChangeWithQueue:(NSMutableDictionary<NSString *,MicroQueueModel *> *)queue {
|
||||
NSLog(@"🔧 mic位变动:开始调用 micCpListByUidList API");
|
||||
|
||||
NSArray<NSString *> *micUserUids = [self getAllMicUserUidsWithQueue:queue];
|
||||
|
||||
if (micUserUids.count == 0) {
|
||||
NSLog(@"🔧 mic位变动:没有相关用户在mic上,跳过API调用");
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用 micCpListByUidList 方法
|
||||
if ([micUserUids containsObject:[AccountInfoStorage instance].getUid]) {
|
||||
NSLog(@"🔧 mic位变动,且包含了当前用户:调用 micCpListByUidList: %@", micUserUids);
|
||||
[self.presenter micCpListByUidList:micUserUids];
|
||||
}
|
||||
|
||||
NSLog(@"🔧 mic位变动:micCpListByUidList API调用完成");
|
||||
}
|
||||
|
||||
/// 初始化当前用户的麦位状态
|
||||
- (void)initializeCurrentUserMicStatus {
|
||||
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(@"🔧 初始化完成 - 当前没有麦位数据");
|
||||
}
|
||||
|
||||
// 延迟设置初始化完成标志,确保进房时的麦位更新不会触发API调用
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
self.hasCompletedRoomInitialization = YES;
|
||||
NSLog(@"🔧 进房初始化完成,后续麦位变动将触发 micCpListByUidList 调用");
|
||||
});
|
||||
}
|
||||
|
||||
/// 获取当前用户的麦位状态
|
||||
- (NSDictionary *)getCurrentUserMicStatus:(NSMutableDictionary<NSString *,MicroQueueModel *> *)queue {
|
||||
NSInteger currentUid = [AccountInfoStorage instance].getUid.integerValue;
|
||||
BOOL isOnMic = NO;
|
||||
NSInteger micPosition = -1;
|
||||
|
||||
for (NSString *position in queue.allKeys) {
|
||||
MicroQueueModel *micModel = queue[position];
|
||||
if (micModel.userInfo.uid > 0 && micModel.userInfo.uid == currentUid) {
|
||||
isOnMic = YES;
|
||||
micPosition = position.integerValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return @{
|
||||
@"isOnMic": @(isOnMic),
|
||||
@"micPosition": @(micPosition)
|
||||
};
|
||||
}
|
||||
|
||||
/// 检查当前用户麦位状态是否发生变化(上麦、下麦、换位)
|
||||
- (BOOL)checkCurrentUserMicStatusChanged:(NSMutableDictionary<NSString *,MicroQueueModel *> *)queue {
|
||||
NSDictionary *currentStatus = [self getCurrentUserMicStatus:queue];
|
||||
BOOL currentUserOnMic = [currentStatus[@"isOnMic"] boolValue];
|
||||
NSInteger currentMicPosition = [currentStatus[@"micPosition"] integerValue];
|
||||
|
||||
// 情况1:之前不在麦上,现在在麦上(上麦)
|
||||
if (currentUserOnMic && !self.currentUserWasOnMic) {
|
||||
NSLog(@"🔧 检测到当前用户刚刚上麦,麦位: %ld", (long)currentMicPosition);
|
||||
return YES;
|
||||
}
|
||||
|
||||
// 情况2:之前在麦上,现在不在麦上(下麦)
|
||||
if (!currentUserOnMic && self.currentUserWasOnMic) {
|
||||
NSLog(@"🔧 检测到当前用户刚刚下麦,之前麦位: %ld", (long)self.currentUserMicPosition);
|
||||
return YES;
|
||||
}
|
||||
|
||||
// 情况3:之前在麦上,现在也在麦上,但位置不同(换位)
|
||||
if (currentUserOnMic && self.currentUserWasOnMic &&
|
||||
currentMicPosition != self.currentUserMicPosition) {
|
||||
NSLog(@"🔧 检测到当前用户换麦位,从 %ld 换到 %ld",
|
||||
(long)self.currentUserMicPosition, (long)currentMicPosition);
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
/// 更新当前用户的麦位状态
|
||||
- (void)updateCurrentUserMicStatus:(NSMutableDictionary<NSString *,MicroQueueModel *> *)queue {
|
||||
NSDictionary *currentStatus = [self getCurrentUserMicStatus:queue];
|
||||
BOOL currentUserOnMic = [currentStatus[@"isOnMic"] boolValue];
|
||||
NSInteger micPosition = [currentStatus[@"micPosition"] integerValue];
|
||||
|
||||
// 检查麦位状态是否发生变化(上麦、下麦、换位)
|
||||
if ([self checkCurrentUserMicStatusChanged:queue]) {
|
||||
self.currentUserMicStatusChanged = YES;
|
||||
}
|
||||
|
||||
// 更新状态
|
||||
self.currentUserWasOnMic = currentUserOnMic;
|
||||
self.currentUserMicPosition = micPosition;
|
||||
}
|
||||
|
||||
/// 发送麦位关系 NIM message
|
||||
- (void)sendMicRelationshipNIMessage:(NSArray<MicCpInfoModel *> *)cpList {
|
||||
NSLog(@"🔧 发送麦位关系 NIM message,CP数据条数: %ld,当前用户麦位: %ld",
|
||||
(long)cpList.count, (long)self.currentUserMicPosition);
|
||||
|
||||
if (!self.roomInfo) {
|
||||
NSLog(@"⚠️ 发送麦位关系 NIM message:roomInfo为空,跳过发送");
|
||||
return;
|
||||
}
|
||||
|
||||
// 将 CP 数据转换为 JSON string
|
||||
NSMutableArray *cpDataArray = [NSMutableArray array];
|
||||
for (MicCpInfoModel *cpInfo in cpList) {
|
||||
NSDictionary *cpDict = @{
|
||||
@"uid": @(cpInfo.uid),
|
||||
@"loverUid": @(cpInfo.loverUid),
|
||||
@"cpLevel": @(cpInfo.cpLevel)
|
||||
};
|
||||
[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];
|
||||
if (error) {
|
||||
NSLog(@"❌ 发送麦位关系 NIM message:JSON序列化失败 - %@", error.localizedDescription);
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
NSLog(@"🔧 发送麦位关系 NIM message:JSON数据 - %@", jsonString);
|
||||
|
||||
// 创建 AttachmentModel
|
||||
AttachmentModel *attachment = [[AttachmentModel alloc] init];
|
||||
attachment.first = MicRelationship_Type; // 1001
|
||||
attachment.second = MicRelationship_CP; // 10011
|
||||
attachment.data = jsonString;
|
||||
|
||||
// 创建 NIM message
|
||||
NIMMessage *message = [[NIMMessage alloc] init];
|
||||
NIMCustomObject *object = [[NIMCustomObject alloc] init];
|
||||
object.attachment = attachment;
|
||||
message.messageObject = object;
|
||||
|
||||
// 设置用户信息到 remoteExt
|
||||
UserInfoModel *userInfo = [self getUserInfo];
|
||||
XPMessageRemoteExtModel *extModel = [[XPMessageRemoteExtModel alloc] init];
|
||||
extModel.androidBubbleUrl = userInfo.androidBubbleUrl;
|
||||
extModel.iosBubbleUrl = userInfo.iosBubbleUrl;
|
||||
extModel.fromSayHelloChannel = userInfo.fromSayHelloChannel;
|
||||
extModel.platformRole = userInfo.platformRole;
|
||||
NSMutableDictionary *remoteExt = [NSMutableDictionary dictionaryWithObject:extModel.model2dictionary forKey:[NSString stringWithFormat:@"%ld", userInfo.uid]];
|
||||
message.remoteExt = remoteExt;
|
||||
|
||||
// 构造会话
|
||||
NSString *sessionID = [NSString stringWithFormat:@"%ld", self.roomInfo.roomId];
|
||||
NIMSession *session = [NIMSession session:sessionID type:NIMSessionTypeChatroom];
|
||||
|
||||
// 发送消息
|
||||
[[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session completion:^(NSError * _Nullable error) {
|
||||
if (error) {
|
||||
NSLog(@"❌ 发送麦位关系 NIM message 失败 - %@", error.localizedDescription);
|
||||
} else {
|
||||
NSLog(@"✅ 发送麦位关系 NIM message 成功");
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
/// 更新 MicMidpointRectManager 的缓存
|
||||
- (void)updateMicMidpointRectManagerCache:(NSArray<MicCpInfoModel *> *)cpList {
|
||||
if (!self.stageView) {
|
||||
NSLog(@"<22><> 更新缓存:stageView 为空,跳过缓存更新");
|
||||
return;
|
||||
}
|
||||
|
||||
MicMidpointRectManager *midpointRectManager = (MicMidpointRectManager *)[self.stageView performSelector:@selector(midpointRectManager)];
|
||||
if (midpointRectManager) {
|
||||
// 获取当前用户及其相邻用户的UID列表
|
||||
NSMutableDictionary<NSString *,MicroQueueModel *> *currentQueue = [self.stageView getMicroQueue];
|
||||
NSArray<NSString *> *micUserUids = [self getAllMicUserUidsWithQueue:currentQueue];
|
||||
|
||||
// 转换为 NSNumber 数组
|
||||
NSMutableArray<NSNumber *> *uidNumbers = [NSMutableArray array];
|
||||
for (NSString *uidString in micUserUids) {
|
||||
[uidNumbers addObject:@(uidString.integerValue)];
|
||||
}
|
||||
|
||||
// 更新缓存:先移除相关用户的旧数据,再合并新数据
|
||||
[midpointRectManager mergeCpListCache:cpList replaceForUids:uidNumbers];
|
||||
|
||||
NSLog(@"<22><> 更新缓存完成:CP数据条数 %lu,相关用户 %@",
|
||||
(unsigned long)cpList.count, uidNumbers);
|
||||
}
|
||||
}
|
||||
|
||||
/// 更新 MicMidpointRectManager 的麦位快照
|
||||
- (void)updateMicMidpointRectManagerSnapshot {
|
||||
if (!self.stageView) {
|
||||
NSLog(@"<22><> 更新快照:stageView 为空,跳过快照更新");
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取当前舞台类型的麦位总数
|
||||
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) {
|
||||
[midpointRectManager rebuildMicSnapshotWithStageView:self.stageView micCount:micCount];
|
||||
NSLog(@"🔧 更新麦位快照完成:麦位总数 %ld", (long)micCount);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
Reference in New Issue
Block a user