// // XPRoomMessageView.m // xplan-ios // // Created by 冯硕 on 2021/10/11. // #import "XPRoomMessageContainerView.h" ///Third #import #import #import ///Tool #import "ThemeColor.h" #import "XPMacro.h" #import "XPRoomMessageConstant.h" #import "XPRoomMessageParser.h" ///Model #import "RoomInfoModel.h" #import "AttachMentModel.h" ///View #import "XPRoomMessageTableViewCell.h" #import "XPRoomMessageHeaderView.h" #import "View/XPRoomMessageHeaderView.h" @interface XPRoomMessageContainerView () ///房间的代理 @property (nonatomic,weak) id hostDelegate; ///列表 @property (nonatomic,strong) UITableView *messageTableView; ///头部 @property (nonatomic,strong) XPRoomMessageHeaderView *headerView; ///底部有新的消息 @property (nonatomic,strong) UIButton *messageTipsBtn; ///是否处于正在爬楼 @property (nonatomic,assign) BOOL isPending; ///数据源 @property (nonatomic,strong) NSMutableArray *datasource; ///临时存放消息的数组 @property (nonatomic,strong) NSMutableArray *incomingMessages; ///messageView 持有这个工具类 进行数据的分发操作 TODO: 有需要在改 @property (nonatomic,strong) XPRoomMessageParser *messageParser; @end @implementation XPRoomMessageContainerView - (instancetype)initWithDelegate:(id)delegate { self = [super init]; if (self) { self.hostDelegate = delegate; [[NIMSDK sharedSDK].chatManager addDelegate:self]; [self initSubViews]; [self initSubViewConstraints]; } return self; } #pragma mark - Response - (void)messageTipsBtnAction:(UIButton *)sender { self.isPending = NO; self.messageTipsBtn.hidden = YES; [self appendAndScrollToBottom]; } #pragma mark - Private Method - (void)initSubViews { [self addSubview:self.messageTableView]; [self addSubview:self.messageTipsBtn]; self.messageTableView.tableHeaderView = self.headerView; } - (void)initSubViewConstraints { [self.messageTableView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.mas_equalTo(self).offset(15); make.top.bottom.right.mas_equalTo(self); }]; [self.messageTipsBtn mas_makeConstraints:^(MASConstraintMaker *make) { make.width.mas_equalTo(100); make.height.mas_equalTo(30); make.bottom.mas_equalTo(self.mas_bottom).offset(-5); make.left.mas_equalTo(self); }]; } ///是否是当前房间 - (BOOL)isCurrentRoom:(NSString *)sessionId { if ([sessionId isEqualToString:[NSString stringWithFormat:@"%ld", self.hostDelegate.getRoomInfo.roomId]]) { return YES; } return NO; } #pragma mark - 添加数据并且做自动滚动 ///添加信息 - (void)addRoomMessage:(NIMMessage *)message { [self.incomingMessages addObject:message]; if (self.isPending) { self.messageTipsBtn.hidden = NO; } else { [self appendAndScrollToBottom]; } } ///追加数据源 - (void)appendAndScrollToBottom { if (self.incomingMessages.count < 1) { return; } if (self.datasource.count > kRoomMessageMaxLength) { NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, kRoomMessageMaxLength/2)]; NSArray *needRemoveMsgArray = [self.datasource objectsAtIndexes:set]; [self.datasource removeObjectsInArray:needRemoveMsgArray]; } // 执行插入 NSMutableArray *indexPaths = [NSMutableArray array]; for (NIMMessage *item in self.incomingMessages) { [self.datasource addObject:[self.messageParser parseMessageAttribute:item]]; [indexPaths addObject:[NSIndexPath indexPathForRow:self.datasource.count - 1 inSection:0]]; } [self.incomingMessages removeAllObjects]; [self.messageTableView reloadData]; //执行插入动画并滚动 [self scrollToBottom:YES]; } ///执行插入动画并滚动 - (void)scrollToBottom:(BOOL)animated { NSInteger s = [self.messageTableView numberOfSections]; //有多少组 if (s<1) return; NSInteger r = [self.messageTableView numberOfRowsInSection:s-1]; //最后一组行 if (r<1) return; NSIndexPath *ip = [NSIndexPath indexPathForRow:r-1 inSection:s-1]; //取最后一行数据 [self.messageTableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionBottom animated:animated]; //滚动到最后一行 } ///自定义消息 是否可以加到 公屏 需要自己维护 - (BOOL)isCanDisplayMessage:(NIMMessage *)message { NIMCustomObject *obj = (NIMCustomObject *)message.messageObject; if (obj.attachment != nil && [obj.attachment isKindOfClass:[AttachmentModel class]]) { AttachmentModel *attachment = (AttachmentModel *)obj.attachment; return [[[self supportMessageDic] objectForKey:@(attachment.first)] containsObject:@(attachment.second)]; } return NO; } - (NSDictionary *)supportMessageDic { return @{ @(CustomMessageType_AllMicroSend): [NSSet setWithObjects: @(Custom_Message_Sub_AllMicroSend), @(Custom_Message_Sub_AllMicroLuckySend), @(Custom_Message_Sub_AllBatchSend), @(Custom_Message_Sub_AllBatchMicroLuckySend), nil], @(CustomMessageType_Gift): [NSSet setWithObjects: @(Custom_Message_Sub_Gift_Send), @(Custom_Message_Sub_Gift_LuckySend), nil], @(CustomMessageType_Room_Tip): [NSSet setWithObjects: @(Custom_Message_Sub_Room_Tip_ShareRoom), @(Custom_Message_Sub_Room_Tip_Attention_Owner), nil], @(CustomMessageType_Kick_User): [NSSet setWithObjects: @(Custom_Message_Sub_Kick_BeKicked), @(Custom_Message_Sub_Kick_BlackList), nil], @(CustomMessageType_Queue): [NSSet setWithObjects: @(Custom_Message_Sub_Queue_Kick), nil], @(CustomMessageType_Candy_Tree): [NSSet setWithObjects: @(Custom_Message_Sub_Candy_Tree_Me), @(Custom_Message_Sub_Candy_Tree_InRoom), @(Custom_Message_Sub_Candy_Tree_AllRoom), @(Custom_Message_Sub_Candy_Tree_AllRoom_Notify), @(Custom_Message_Sub_Candy_Tree_InRoom_NeedAllMicSend), nil], }; } #pragma mark - NIMChatManagerDelegate - (void)onRecvMessages:(NSArray *)messages { for (NIMMessage * message in messages) { [self handleMessage:message]; } } //发送消息成功回调 - (void)sendMessage:(NIMMessage *)message didCompleteWithError:(NSError *)error { [self handleMessage:message]; } - (void)handleMessage:(NIMMessage *)message { ///房间内的消息 if (message.session.sessionType == NIMSessionTypeChatroom && [self isCurrentRoom:message.session.sessionId]) { if (message.messageType == NIMMessageTypeText || message.messageType == NIMMessageTypeTip) { [self addRoomMessage:message]; } else if (message.messageType == NIMMessageTypeNotification) { NIMNotificationObject *notiMsg = (NIMNotificationObject *)message.messageObject; NIMChatroomNotificationContent *content = (NIMChatroomNotificationContent *)notiMsg.content; if (content.eventType == NIMChatroomEventTypeEnter) { [self addRoomMessage:message]; } }else if (message.messageType == NIMMessageTypeCustom) {// 如果是自定义消息 ///TODO : 处理自定义消息 if ([self isCanDisplayMessage:message]) { [self addRoomMessage:message]; } } } else { } } #pragma mark - ScrollViewDelegate - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { // 手动拖拽开始 self.isPending = YES; } - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { // 手动拖拽结束(decelerate:0松手时静止;1松手时还在运动,会触发DidEndDecelerating方法) if (!decelerate) { [self finishDraggingWith:scrollView]; } } - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { // 静止后触发(手动) [self finishDraggingWith:scrollView]; } - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView { if (!self.isPending && self.incomingMessages.count <= 0) { [self scrollToBottom:YES]; } } /** 手动拖拽动作彻底完成(减速到零) */ - (void)finishDraggingWith:(UIScrollView *)scrollView { CGFloat contentSizeH = scrollView.contentSize.height; CGFloat contentOffsetY = scrollView.contentOffset.y; CGFloat sizeH = scrollView.frame.size.height; self.isPending = contentSizeH - contentOffsetY - sizeH > 20.0; if (!self.isPending) { self.messageTipsBtn.hidden = YES; if (self.incomingMessages.count > 0) { [self appendAndScrollToBottom]; } } } #pragma mark - UITableViewDelegate And UITableViewDataSource - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.datasource.count; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { NSAttributedString* attr = [self.datasource objectAtIndex:indexPath.row]; CGSize maxSize = CGSizeMake(kRoomMessageMaxWidth, MAXFLOAT); YYTextLayout *layout = [YYTextLayout layoutWithContainerSize:maxSize text:attr]; return layout.textBoundingSize.height + kRoomMessageTextSpaceHeight * 2 + 10; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { XPRoomMessageTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([XPRoomMessageTableViewCell class])]; if (cell == nil) { cell = [[XPRoomMessageTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:NSStringFromClass([XPRoomMessageTableViewCell class])]; } NSAttributedString* attr = [self.datasource objectAtIndex:indexPath.row]; cell.attributedString = attr; return cell; } #pragma mark - Getters And Setters - (UITableView *)messageTableView { if (!_messageTableView) { _messageTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; _messageTableView.delegate = self; _messageTableView.dataSource = self; _messageTableView.tableFooterView = [UIView new]; _messageTableView.separatorStyle = UITableViewCellSeparatorStyleNone; _messageTableView.backgroundColor = [UIColor clearColor]; _messageTableView.showsVerticalScrollIndicator = NO; if (@available(iOS 11.0, *)) { _messageTableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; } [_messageTableView registerClass:[XPRoomMessageTableViewCell class] forCellReuseIdentifier:NSStringFromClass([XPRoomMessageTableViewCell class])]; } return _messageTableView; } - (XPRoomMessageHeaderView *)headerView { if (!_headerView) { _headerView = [[XPRoomMessageHeaderView alloc] init]; } return _headerView; } - (UIButton *)messageTipsBtn { if (!_messageTipsBtn) { _messageTipsBtn = [UIButton buttonWithType:UIButtonTypeCustom]; [_messageTipsBtn setTitle:@"底部有新消息" forState:UIControlStateNormal]; [_messageTipsBtn setTitleColor:[ThemeColor appMainColor] forState:UIControlStateNormal]; _messageTipsBtn.layer.cornerRadius = 15.0; _messageTipsBtn.layer.masksToBounds = YES; _messageTipsBtn.titleLabel.font = [UIFont systemFontOfSize:12.0]; _messageTipsBtn.backgroundColor = [UIColor whiteColor]; [_messageTipsBtn addTarget:self action:@selector(messageTipsBtnAction:) forControlEvents:UIControlEventTouchUpInside]; _messageTipsBtn.hidden = YES; } return _messageTipsBtn; } - (NSMutableArray *)datasource { if (!_datasource) { _datasource = [NSMutableArray array]; } return _datasource; } - (NSMutableArray *)incomingMessages { if (!_incomingMessages) { _incomingMessages = [NSMutableArray array]; } return _incomingMessages; } - (XPRoomMessageParser *)messageParser { if (!_messageParser) { _messageParser = [[XPRoomMessageParser alloc] init]; _messageParser.hostDelegate = self.hostDelegate; } return _messageParser; } @end