// // XPRoomViewController.m // xplan-ios // // Created by 冯硕 on 2021/10/11. // #import "XPRoomViewController.h" ///Third #import #import ///Tool #import "XPMacro.h" #import "XPConstant.h" #import "AccountInfoStorage.h" #import "RtcManager.h" #import "XPRoomMiniManager.h" #import "TTPopup.h" #import "Api+ArrangeMic.h" #import "DESEncrypt.h" ///Model #import "RoomInfoModel.h" #import "UserInfoModel.h" #import "AttachmentModel.h" #import "ArrangeMicModel.h" ///View #import "BaseNavigationController.h" #import "XPRoomActivityContainerView.h" #import "XPRoomBackContainerView.h" #import "XPRoomMenuContainerView.h" #import "XPRoomMessageContainerView.h" #import "RoomHeaderView.h" #import "SocialStageView.h" #import "DatingStageView.h" #import "XPRoomAnimationView.h" #import "XPRoomFunctionContainerView.h" #import "XPArrangeMicViewController.h" #import "XPRoomSettingViewController.h" #import "XPRoomSettingInputView.h" ///P #import "XPRoomPresenter.h" #import "XPRoomProtocol.h" #import "RoomHostDelegate.h" #import "RoomGuestDelegate.h" UIKIT_EXTERN NSString * const kRoomGiftEffectUpdateNotificationKey; UIKIT_EXTERN NSString * const kRoomRoomSettingNotificationKey; UIKIT_EXTERN NSString * const kRoomMiniNotificationKey; @interface XPRoomViewController () ///背景 @property (nonatomic,strong) XPRoomBackContainerView *backContainerView; ///房间信息 @property (nonatomic,strong) RoomHeaderView *roomHeaderView; ///坑位信息 @property (nonatomic,strong) StageView *stageView; ///相亲的坑位信息 @property (nonatomic,strong) DatingStageView *datingView; ///普通坑位的view @property (nonatomic,strong) SocialStageView *socialView; ///公屏 @property (nonatomic,strong) XPRoomMessageContainerView *messageContainerView; ///底部操作栏 @property (nonatomic,strong) XPRoomMenuContainerView *menuContainerView; ///活动 @property (nonatomic,strong) XPRoomActivityContainerView *activityContainerView; ///动画的view @property (nonatomic,strong) XPRoomAnimationView *animationView; ///功能view的的容器 @property (nonatomic,strong) XPRoomFunctionContainerView *functionView; ///房间的Uid @property (nonatomic,copy) NSString * roomUid; ///用户信息 @property (nonatomic,strong) UserInfoModel *userInfo; ///房间信息 @property (nonatomic,strong) RoomInfoModel *roomInfo; ///本地是否修改过礼物特效显示 当前房间如果设置过礼物特效是否显示的话 就不再跟随房间礼物特效的更新而更新 @property (nonatomic,assign) BOOL hasAnimationEffect; @end @implementation XPRoomViewController + (BOOL)openRoom:(NSString *)roomUid viewController:(UIViewController *)viewController { XPRoomViewController * roomVC = [[self alloc] init]; roomVC.roomUid = roomUid; BaseNavigationController * baseNav = [[BaseNavigationController alloc] initWithRootViewController:roomVC]; baseNav.modalPresentationStyle = UIModalPresentationFullScreen; [viewController presentViewController:baseNav animated:YES completion:nil]; return YES; } - (void)dealloc { #warning to dox - 时刻注意这个方法 功能做完的时候删除此方法 } - (XPRoomPresenter *)createPresenter { return [[XPRoomPresenter alloc] init]; } - (BOOL)isHiddenNavBar { return YES; } - (void)viewDidLoad { [super viewDidLoad]; [self exitOldRoom]; [self initSubViews]; [self initSubViewConstraints]; [self.presenter initEnterRoom:self.roomUid user:[AccountInfoStorage instance].getUid]; /// 公屏 福袋礼物爆出的礼物 需要依赖 当前房间中的礼物列表 需要提前获取礼物列表 可以拿到礼物 没有想到别的方法去解决这个问题 有需要改的在修改吧 [self.presenter getNormalGiftList:self.roomUid]; //监听云信消息 [[NIMSDK sharedSDK].chatroomManager addDelegate:self]; [[NIMSDK sharedSDK].chatManager addDelegate:self]; [[NIMSDK sharedSDK].loginManager addDelegate:self]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(myGiftEffectUpdate:) name:kRoomGiftEffectUpdateNotificationKey object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(gotoRoomSetting:) name:kRoomRoomSettingNotificationKey object:nil]; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self.view endEditing:YES]; } #pragma mark - Private Method - (void)initSubViews { [self.view addSubview:self.backContainerView]; [self.view addSubview:self.roomHeaderView]; self.stageView = self.socialView; [self.view addSubview:self.stageView]; [self.view addSubview:self.messageContainerView]; [self.view addSubview:self.menuContainerView]; [self.view addSubview:self.activityContainerView]; [self.view addSubview:self.functionView]; [self.view addSubview:self.animationView]; } - (void)initSubViewConstraints { [self.backContainerView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.mas_equalTo(self.view); }]; [self.roomHeaderView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.top.mas_equalTo(self.view); make.height.mas_equalTo(kNavigationHeight); }]; [self.stageView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.mas_equalTo(self.view); make.top.mas_equalTo(self.roomHeaderView.mas_bottom); make.height.mas_equalTo(self.stageView.hightForStageView); }]; [self.messageContainerView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.stageView.mas_bottom); make.bottom.equalTo(self.menuContainerView.mas_top).offset(-5); make.left.equalTo(self.view); make.right.equalTo(self.activityContainerView.mas_left).offset(-10); }]; [self.menuContainerView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.mas_equalTo(self.view); make.bottom.mas_equalTo(-kSafeAreaBottomHeight- 8); make.height.mas_equalTo(40); }]; [self.activityContainerView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.mas_equalTo(self.messageContainerView); make.right.mas_equalTo(self.view); make.bottom.mas_equalTo(self.menuContainerView.mas_top).offset(-5); make.width.mas_equalTo(80); }]; [self.animationView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.mas_equalTo(self.view); }]; [self.functionView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.mas_equalTo(self.view); }]; } - (void)exitOldRoom { NSString * roomUid = [NSString stringWithFormat:@"%ld", [XPRoomMiniManager shareManager].getRoomInfo.uid]; if (roomUid.integerValue > 0 && roomUid.integerValue != self.roomUid.integerValue) { //有最小化房间 并且要进入的房间和最小化的房间不一样的话 就先退掉 最小化的房间 NSString * roomId = [NSString stringWithFormat:@"%ld", [XPRoomMiniManager shareManager].getRoomInfo.roomId]; [self.presenter exitNIMRoom:roomId]; [[RtcManager instance] exitRoom]; } } - (void)userEnterRoomSuccess { if (self.userInfo.carEffect.length > 0) { AttachmentModel *attachment = [[AttachmentModel alloc]init]; attachment.first = CustomMessageType_Car_Notify; attachment.second = Custom_Message_Sub_Car_EnterRoom; NSMutableDictionary *att = [NSMutableDictionary dictionary]; [att setValue:self.userInfo.carEffect forKey:@"effect"]; [att setValue:self.userInfo.nick forKey:@"nick"]; attachment.data = att; NSString *sessionID = [NSString stringWithFormat:@"%ld",self.roomInfo.roomId]; NIMMessage *message = [[NIMMessage alloc]init]; NIMCustomObject *object = [[NIMCustomObject alloc] init]; object.attachment = attachment; message.messageObject = object; //构造会话 NIMSession *session = [NIMSession session:sessionID type:NIMSessionTypeChatroom]; [[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:nil]; } } - (void)changeStageViewOnRoomUpdate { if (self.roomInfo.roomModeType == RoomModeType_Open_Blind) { if (self.stageView == self.datingView) { return; } [self.stageView removeFromSuperview]; self.stageView = self.datingView; } else { if (self.stageView == self.socialView) { return; } [self.stageView removeFromSuperview]; self.stageView = self.socialView; } if (!self.stageView.superview) { [self.view insertSubview:self.stageView aboveSubview:self.roomHeaderView]; } [self.stageView mas_remakeConstraints:^(MASConstraintMaker *make) { make.left.right.mas_equalTo(self.view); make.top.mas_equalTo(self.roomHeaderView.mas_bottom); make.height.mas_equalTo(self.stageView.hightForStageView); }]; [self.messageContainerView mas_remakeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.stageView.mas_bottom); make.bottom.equalTo(self.menuContainerView.mas_top).offset(-5); make.left.equalTo(self.view); make.right.equalTo(self.activityContainerView.mas_left).offset(-10); }]; [self.activityContainerView mas_remakeConstraints:^(MASConstraintMaker *make) { make.top.mas_equalTo(self.messageContainerView); make.right.mas_equalTo(self.view); make.bottom.mas_equalTo(self.menuContainerView.mas_top).offset(-5); make.width.mas_equalTo(80); }]; } #pragma mark - 本地礼物特效更新 - (void)myGiftEffectUpdate:(NSNotification *)notification { NSDictionary * dic = notification.object; self.roomInfo.hasAnimationEffect = ((NSNumber *)dic[@"hasAnimationEffect"]).boolValue; self.hasAnimationEffect = self.roomInfo.hasAnimationEffect; [self.roomHeaderView onRoomUpdate]; } ///本来不想用通知的 但是房间设置需要坑位的信息 XPRoomMoreMenuViewController 里面没有包含hostDelegate(也不想传来传去的 ) 就先用通知吧 - (void)gotoRoomSetting:(NSNotification *)notification { XPRoomSettingViewController * roomsettingVC = [[XPRoomSettingViewController alloc] initWithDelegate:self]; [self.navigationController pushViewController:roomsettingVC animated:YES]; } #pragma mark - XPRoomProtocol - (void)initEnterRoomSuccess:(RoomInfoModel *)roomInfo user:(UserInfoModel *)userInfo { self.hasAnimationEffect = roomInfo.hasAnimationEffect; self.roomInfo = roomInfo; self.userInfo = userInfo; if (roomInfo.valid) { // 房间有效,直接进入房间 if (roomInfo.roomPwd.length > 0 && roomInfo.uid != [AccountInfoStorage instance].getUid.integerValue && [XPRoomMiniManager shareManager].getRoomInfo.uid != self.roomUid.integerValue) {//进房密码的情况 XPRoomSettingInputView * titleView = [[XPRoomSettingInputView alloc] init]; titleView.maxCount = 15; titleView.delegate = self; titleView.type = RoomSettingInputType_Input_Pwd; TTPopupConfig * config = [[TTPopupConfig alloc] init]; config.contentView = titleView; config.maskBackgroundAlpha = 1; config.shouldDismissOnBackgroundTouch = NO; [TTPopup popupWithConfig:config]; } else { [self changeStageViewOnRoomUpdate]; [self.roomHeaderView onRoomUpdate]; [self.activityContainerView onRoomUpdate]; [self.menuContainerView onRoomUpdate]; if ([XPRoomMiniManager shareManager].getRoomInfo.uid != self.roomUid.integerValue) {// 最小化进房 还是原来的房间的话 不需要重新进入云信 因为压根没退 [self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo]; } else { //虽然没退出房间 但是队列还是要拿的 [self.stageView onRoomEntered]; } [[XPRoomMiniManager shareManager] configRoomInfo:nil]; } } else { // 房间无效,分是否为自己房间进行处理 if ([self.roomUid isEqualToString:[NSString stringWithFormat:@"%ld", userInfo.uid]]) { // 当前用户进入自己房间 if (roomInfo.roomId > 0) { // 存在房间信息,直接使用房间信息开房 [self.presenter openRoom:roomInfo.title type:roomInfo.type roomPwd:roomInfo.roomPwd roomDesc:roomInfo.roomDesc backPic:@""]; } else { // 不存在房间信息,全新开房 NSString* title = [NSString stringWithFormat:@"%@的房间", userInfo.nick]; [self.presenter openRoom:title type:RoomType_Game roomPwd:@"" roomDesc:@"" backPic:@""]; } } else { // 当前用户进入别人房间 // TODO: 房主已经下线。 [self showSuccessToast:@"房主已下线"]; [self enterRoomFail:0]; } } } - (void)openRoomSuccess:(RoomInfoModel *)roomInfo { self.roomInfo = roomInfo; [self.roomHeaderView onRoomUpdate]; [self.activityContainerView onRoomUpdate]; [self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo]; } - (void)enterRoomSuccess:(NIMChatroom *)chatRoom { // room/get中的房间在线人数 是定时更新的 不是及时刷新 self.roomInfo.onlineNum = chatRoom.onlineUserCount; [self.stageView onRoomEntered]; } - (void)enterRoomFail:(NSInteger)code { if (code == 13003) { [self showErrorToast:@"您已被管理员拉黑"]; } [self dismissViewControllerAnimated:YES completion:nil]; } #pragma mark - NIMChatroomManagerDelegate - (void)chatroomBeKicked:(NIMChatroomBeKickedResult *)result { if (result.reason == 2) { [self showErrorToast:@"您被管理员踢出直播间"]; } else if (result.reason == 5) { [self showErrorToast:@"您已被管理员拉黑"]; } ///退出排麦 if (self.roomInfo.roomModeType == RoomModeType_Open_Micro_Mode) { NSString * roomUid = [NSString stringWithFormat:@"%ld", self.roomInfo.uid]; NSString * uid = [AccountInfoStorage instance].getUid; [Api getRoomArrangeMicList:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) { if (code == 200) { ArrangeMicModel * arrangeMicModel= [ArrangeMicModel modelWithJSON:data.data]; if (arrangeMicModel.myPos.intValue > 0) { [Api cancelArrangeMic:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {} roomUid:roomUid operUid:uid groupType:@"0"]; } } } roomUid:roomUid operUid:uid page:@"1" pageSize:@"50"]; } [self.presenter exitNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId]]; [[RtcManager instance] exitRoom]; [self dismissViewControllerAnimated:YES completion:nil]; } #pragma mark - NIMChatManagerDelegate - (void)onRecvMessages:(NSArray *)messages { for (NIMMessage * message in messages) { // 非房间内消息不处理 if (message.session.sessionType != NIMSessionTypeChatroom) { continue; } // 非本房间不处理 if (![message.session.sessionId isEqualToString:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId]]) { continue; } if (message.messageType == NIMMessageTypeNotification) { NIMNotificationObject *notiMsg = (NIMNotificationObject *)message.messageObject; NIMChatroomNotificationContent *content = (NIMChatroomNotificationContent *)notiMsg.content; NSInteger onLineNumber = self.roomInfo.onlineNum; switch (content.eventType) { case NIMChatroomEventTypeInfoUpdated: // 麦序状态更新 { NSDictionary *data = [content.notifyExt toJSONObject]; int type = [data[@"type"] intValue]; switch (type) { case 1: case 3: { NSMutableDictionary *lastRoomInfoDic = [NSMutableDictionary dictionaryWithDictionary:[self.roomInfo model2dictionary]]; [lastRoomInfoDic addEntriesFromDictionary: ((NSString *)data[@"roomInfo"]).toJSONObject]; RoomInfoModel * lastRoomInfo = [RoomInfoModel modelWithJSON:lastRoomInfoDic]; lastRoomInfo.hasAnimationEffect = self.hasAnimationEffect; self.roomInfo= lastRoomInfo; [self changeStageViewOnRoomUpdate]; [self.stageView onRoomUpdate]; [self.menuContainerView onRoomUpdate]; } break; } } break; case NIMChatroomEventTypeEnter: if (content.source.userId.integerValue != [AccountInfoStorage instance].getUid.integerValue) { onLineNumber += 1; }else {///如果是自己的话 如果有坐骑的话 [self userEnterRoomSuccess]; } break; case NIMChatroomEventTypeAddBlack: case NIMChatroomEventTypeKicked: case NIMChatroomEventTypeExit: onLineNumber -= 1; break; default: break; } self.roomInfo.onlineNum = onLineNumber; [self.roomHeaderView onRoomUpdate]; [self.stageView handleNIMNotificationMessage:message]; [self.animationView handleNIMNotificationMessage:message]; [self.messageContainerView handleNIMNotificationMessage:message]; } else if (message.messageType == NIMMessageTypeCustom) { NIMCustomObject *obj = (NIMCustomObject *)message.messageObject; if (obj.attachment != nil && [obj.attachment isKindOfClass:[AttachmentModel class]]) { AttachmentModel * attachment = obj.attachment; [self.stageView handleNIMCustomMessage:message]; [self.animationView handleNIMCustomMessage:message]; [self.menuContainerView handleNIMCustomMessage:message]; [self.activityContainerView handleNIMCustomMessage:message]; if (attachment.first == CustomMessageType_First_Recharge_Reward && attachment.second == Custom_Message_Sub_Room_First_Recharge_Reward) { self.userInfo.isFirstCharge = NO; [self.menuContainerView onRoomUpdate]; [self.activityContainerView onRoomUpdate]; } else if (attachment.first == CustomMessageType_Update_RoomInfo && attachment.second == Custom_Message_Sub_Update_RoomInfo_MessageState) { ///兼容安卓和iOS 协议不同的问题 NSDictionary * dic= attachment.data[@"roomInfo"]; if (dic.allKeys.count <=0) { dic = attachment.data; } RoomInfoModel * roomInfo = [RoomInfoModel modelWithDictionary:dic]; self.roomInfo.isCloseScreen = roomInfo.isCloseScreen; [self.menuContainerView onRoomUpdate]; } [self.messageContainerView handleNIMCustomMessage:message]; } } else if(message.messageType == NIMMessageTypeText) { [self.messageContainerView handleNIMTextMessage:message]; } } } //发送消息成功回调 - (void)sendMessage:(NIMMessage *)message didCompleteWithError:(NSError *)error { if (error) return; if (message.messageType == NIMMessageTypeCustom) { NIMCustomObject *obj = (NIMCustomObject *)message.messageObject; if (obj.attachment != nil && [obj.attachment isKindOfClass:[AttachmentModel class]]) { AttachmentModel * attachment = obj.attachment; [self.stageView handleNIMCustomMessage:message]; [self.animationView handleNIMCustomMessage:message]; if (attachment.first == CustomMessageType_Update_RoomInfo && attachment.second == Custom_Message_Sub_Update_RoomInfo_MessageState) { RoomInfoModel * roomInfo = [RoomInfoModel modelWithDictionary:attachment.data[@"roomInfo"]]; self.roomInfo.isCloseScreen = roomInfo.isCloseScreen; [self.menuContainerView onRoomUpdate]; } [self.messageContainerView handleNIMCustomMessage:message]; } }else if(message.messageType == NIMMessageTypeText) { [self.messageContainerView handleNIMTextMessage:message]; } } #pragma mark - NIMLoginManagerDelegate - (void)onKickout:(NIMLoginKickoutResult *)result { [self exitRoom]; [self dismissViewControllerAnimated:YES completion:nil]; } #pragma mark - RoomDelegate - (RoomInfoModel *)getRoomInfo { return self.roomInfo; } - (UserInfoModel *)getUserInfo { return self.userInfo; } - (void)exitRoom { if (self.roomInfo.roomModeType == RoomModeType_Open_Micro_Mode) { NSString * roomUid = [NSString stringWithFormat:@"%ld", self.roomInfo.uid]; NSString * uid = [AccountInfoStorage instance].getUid; [Api getRoomArrangeMicList:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) { if (code == 200) { ArrangeMicModel * arrangeMicModel= [ArrangeMicModel modelWithJSON:data.data]; if (arrangeMicModel.myPos.intValue > 0) { [TTPopup alertWithMessage:@"退出房间后将退出目前排麦,再次进入需要重新排麦,确认退出房间吗?" confirmHandler:^{ [Api cancelArrangeMic:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) { } roomUid:roomUid operUid:uid groupType:@"0"]; [self.presenter exitNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId]]; [[RtcManager instance] exitRoom]; [self dismissViewControllerAnimated:YES completion:nil]; } cancelHandler:^{ }]; return; } } [self.presenter exitNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId]]; [[RtcManager instance] exitRoom]; [self dismissViewControllerAnimated:YES completion:nil]; } roomUid:roomUid operUid:uid page:@"1" pageSize:@"50"]; } else { [self.presenter exitNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId]]; [[RtcManager instance] exitRoom]; [self dismissViewControllerAnimated:YES completion:nil]; } } - (void)miniRoom { [[XPRoomMiniManager shareManager] configRoomInfo:self.roomInfo]; NSDictionary * dic = @{@"roomInfo":self.roomInfo.model2dictionary, @"userInfo":self.userInfo.model2dictionary, @"microQueue":self.getMicroQueue}; [[NSNotificationCenter defaultCenter] postNotificationName:kRoomMiniNotificationKey object:nil userInfo:dic]; [self.view endEditing:YES]; [self dismissViewControllerAnimated:YES completion:nil]; } - (UINavigationController *)getCurrentNav { return self.navigationController; } - (NSMutableDictionary *)getMicroQueue { return [self.stageView getMicroQueue]; } - (void)onMicroQueueUpdate:(NSMutableDictionary *)queue { [self.menuContainerView onMicroQueueUpdate:queue]; } - (CGPoint)centerForUserAtUid:(NSString *)uid { return [self.stageView centerForUserAtUid:uid]; } #pragma mark - XPRoomSettingInputViewDelegate ///点击了完成 - (void)didClickConfirm:(NSString *)text type:(RoomSettingInputType)type { NSString *pwdDes = [DESEncrypt encryptUseDES:text key:KeyWithType(KeyType_PasswordEncode)]; if ([pwdDes isEqualToString:self.roomInfo.roomPwd]) { [self.roomHeaderView onRoomEntered]; [self.activityContainerView onRoomEntered]; [self.menuContainerView onRoomEntered]; [self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo]; } else { [self enterRoomFail:0]; } } - (void)didClickCancel:(RoomSettingInputType)type { if (type == RoomSettingInputType_Input_Pwd) { [self enterRoomFail:0]; } } - (XPRoomBackContainerView *)backContainerView { if (!_backContainerView) { _backContainerView = [[XPRoomBackContainerView alloc] initWithdelegate:self]; } return _backContainerView; } - (RoomHeaderView *)roomHeaderView { if (!_roomHeaderView) { _roomHeaderView = [[RoomHeaderView alloc] initWithDelegate:self]; } return _roomHeaderView; } - (SocialStageView *)socialView { if (!_socialView) { _socialView = [[SocialStageView alloc] initWithDelegate:self]; } return _socialView; } - (DatingStageView *)datingView { if (!_datingView) { _datingView = [[DatingStageView alloc] initWithDelegate:self]; } return _datingView; } - (XPRoomMessageContainerView *)messageContainerView { if (!_messageContainerView) { _messageContainerView = [[XPRoomMessageContainerView alloc] initWithDelegate:self]; } return _messageContainerView; } - (XPRoomMenuContainerView *)menuContainerView { if (!_menuContainerView) { _menuContainerView = [[XPRoomMenuContainerView alloc] initWithDelegate:self]; } return _menuContainerView; } - (XPRoomActivityContainerView *)activityContainerView { if (!_activityContainerView) { _activityContainerView = [[XPRoomActivityContainerView alloc] initWithdelegate:self]; } return _activityContainerView; } - (XPRoomAnimationView *)animationView { if (!_animationView) { _animationView = [[XPRoomAnimationView alloc] initWithdelegate:self]; } return _animationView; } - (XPRoomFunctionContainerView *)functionView { if (!_functionView) { _functionView = [[XPRoomFunctionContainerView alloc] initWithdelegate:self]; } return _functionView; } @end