// // TUIRepliesDetailViewController.m // TUIChat // // Created by wyl on 2022/4/27. // Copyright © 2023 Tencent. All rights reserved. // #import "TUIRepliesDetailViewController.h" #import #import "TUIChatDataProvider.h" #import "TUIMessageDataProvider.h" #import "TUITextMessageCell.h" #import "TUITextMessageCellData.h" #import "TUIChatConfig.h" #import #import #import #import #import #import #import "TUIFaceMessageCell.h" #import "TUIFileMessageCell.h" #import "TUIFileViewController.h" #import "TUIImageMessageCell.h" #import "TUIJoinGroupMessageCell.h" #import "TUILinkCell.h" #import "TUIMediaView.h" #import "TUIMergeMessageCell.h" #import "TUIMergeMessageListController.h" #import "TUIMessageDataProvider.h" #import "TUIReferenceMessageCell.h" #import "TUIReplyMessageCell.h" #import "TUIReplyMessageCellData.h" #import "TUITextMessageCell.h" #import "TUIVideoMessageCell.h" #import "TUIVoiceMessageCell.h" #import "TUIMessageCellConfig.h" @interface TUIRepliesDetailViewController () @property(nonatomic, strong) TUIMessageCellData *cellData; @property(nonatomic, strong) TUIMessageDataProvider *msgDataProvider; @property(nonatomic, strong) UIView *headerView; @property(nonatomic, strong) UITableView *tableView; @property(nonatomic, strong) NSArray *imMsgs; @property(nonatomic, strong) NSMutableArray *uiMsgs; @property(nonatomic, assign) BOOL responseKeyboard; @property(nonatomic, strong) TUIChatConversationModel *conversationData; @property(nonatomic, strong) TUIMessageCellConfig *messageCellConfig; @property(nonatomic, strong) TUIMessageCellLayout *originCellLayout; @property TMsgDirection direction; @property(nonatomic, assign) BOOL showName; @property(nonatomic, assign) BOOL showMessageTime; @property(nonatomic) BOOL isMsgNeedReadReceipt; @end @implementation TUIRepliesDetailViewController - (instancetype)initWithCellData:(TUIMessageCellData *)data conversationData:(TUIChatConversationModel *)conversationData { self = [super init]; self.cellData = data; [self setConversation:conversationData]; return self; } - (void)viewDidLoad { [super viewDidLoad]; [self setupViews]; [self setupInputViewController]; [[V2TIMManager sharedInstance] addAdvancedMsgListener:self]; [TUICore registerEvent:TUICore_TUIPluginNotify subKey:TUICore_TUIPluginNotify_DidChangePluginViewSubKey object:self]; } - (void)viewWillAppear:(BOOL)animated { [self updateRootMsg]; [self applyData]; [self updateTableViewConstraint]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; self.responseKeyboard = YES; self.isMsgNeedReadReceipt = YES; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; self.responseKeyboard = NO; [self revertRootMsg]; if (self.willCloseCallback) { self.willCloseCallback(); } } - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; if (self.inputController.status == Input_Status_Input || self.inputController.status == Input_Status_Input_Keyboard) { CGPoint offset = self.tableView.contentOffset; __weak typeof(self) weakSelf = self; dispatch_async(dispatch_get_main_queue(), ^{ __strong typeof(weakSelf) strongSelf = weakSelf; strongSelf.responseKeyboard = YES; [UIApplication.sharedApplication.keyWindow endEditing:YES]; [strongSelf inputController:strongSelf.inputController didChangeHeight:CGRectGetMaxY(strongSelf.inputController.inputBar.frame) + Bottom_SafeHeight]; [strongSelf.tableView setContentOffset:offset]; }); } } - (void)dealloc { NSLog(@"%s dealloc", __FUNCTION__); [TUICore unRegisterEventByObject:self]; } - (void)applyData { NSArray *messageModifyReplies = self.cellData.messageModifyReplies; NSMutableArray *msgIDArray = [NSMutableArray array]; if (messageModifyReplies.count > 0) { for (NSDictionary *dic in messageModifyReplies) { if (dic) { NSString *messageID = dic[@"messageID"]; if (IS_NOT_EMPTY_NSSTRING(messageID)) { [msgIDArray addObject:messageID]; } } } } // When the only reply is retracted, go back to the previous controller if (msgIDArray.count <= 0) { [self.navigationController popViewControllerAnimated:YES]; } __weak typeof(self) weakSelf = self; [TUIChatDataProvider findMessages:msgIDArray callback:^(BOOL succ, NSString *_Nonnull error_message, NSArray *_Nonnull msgs) { __strong typeof(weakSelf) strongSelf = weakSelf; if (succ) { if (msgs.count > 0) { strongSelf.imMsgs = msgs; strongSelf.uiMsgs = [self transUIMsgFromIMMsg:msgs]; dispatch_async(dispatch_get_main_queue(), ^{ if (strongSelf.uiMsgs.count != 0) { [strongSelf.tableView reloadData]; [strongSelf.tableView layoutIfNeeded]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [strongSelf scrollToBottom:NO]; }); } }); } } }]; } - (void)updateTableViewConstraint { CGFloat textViewHeight = TUIChatConfig.defaultConfig.enableMainPageInputBar? TTextView_Height:0; CGFloat height = textViewHeight + Bottom_SafeHeight; CGRect msgFrame = self.tableView.frame; msgFrame.size.height = self.view.frame.size.height - height; self.tableView.frame = msgFrame; } - (void)setupViews { self.title = TIMCommonLocalizableString(TUIKitRepliesDetailTitle); self.view.backgroundColor = TUIChatDynamicColor(@"chat_controller_bg_color", @"#FFFFFF"); self.tableView.scrollsToTop = NO; self.tableView.delegate = self; self.tableView.dataSource = self; [self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone]; self.tableView.contentInset = UIEdgeInsetsMake(5, 0, 0, 0); [self.messageCellConfig bindTableView:self.tableView]; } - (void)setupInputViewController { _inputController = [[TUIInputController alloc] init]; _inputController.delegate = self; _inputController.view.frame = CGRectMake(0, self.view.frame.size.height - TTextView_Height - Bottom_SafeHeight, self.view.frame.size.width, TTextView_Height + Bottom_SafeHeight); _inputController.view.autoresizingMask = UIViewAutoresizingFlexibleTopMargin; _inputController.inputBar.isFromReplyPage = YES; [self addChildViewController:_inputController]; [self.view addSubview:_inputController.view]; TUIFaceGroup *group = TIMConfig.defaultConfig.faceGroups[0]; [_inputController.faceSegementScrollView setItems:(id) @[ group ] delegate:(id)_inputController]; TUIMenuCellData *data = [[TUIMenuCellData alloc] init]; data.path = group.menuPath; data.isSelected = YES; [_inputController.menuView setData:(id) @[ data ]]; _inputController.view.hidden = !TUIChatConfig.defaultConfig.enableMainPageInputBar; CGFloat margin = 0; CGFloat padding = 10; _inputController.inputBar.inputTextView.frame = CGRectMake(margin, _inputController.inputBar.inputTextView.frame.origin.y, _inputController.inputBar.frame.size.width - _inputController.inputBar.faceButton.frame.size.width - margin * 2 - padding, _inputController.inputBar.inputTextView.frame.size.height); _inputController.inputBar.faceButton.frame = CGRectMake(_inputController.inputBar.frame.size.width - _inputController.inputBar.faceButton.frame.size.width - margin, _inputController.inputBar.faceButton.frame.origin.y, _inputController.inputBar.faceButton.frame.size.width, _inputController.inputBar.faceButton.frame.size.height); if (_inputController.inputBar.micButton) { _inputController.inputBar.micButton.alpha = 0; } if (_inputController.inputBar.moreButton) { _inputController.inputBar.moreButton.alpha = 0; } [_inputController.inputBar defaultLayout]; } - (void)updateRootMsg { self.originCellLayout = self.cellData.cellLayout; self.direction = self.cellData.direction; self.showName = self.cellData.showName; self.showMessageTime = self.cellData.showMessageTime; TUIMessageCellData *data = self.cellData; TUIMessageCellLayout *layout = TUIMessageCellLayout.incommingMessageLayout; if ([data isKindOfClass:TUITextMessageCellData.class]) { layout = TUIMessageCellLayout.incommingTextMessageLayout; } if ([data isKindOfClass:TUIReferenceMessageCellData.class]) { layout = TUIMessageCellLayout.incommingTextMessageLayout; } if ([data isKindOfClass:TUIVoiceMessageCellData.class]) { layout = [TUIMessageCellLayout incommingVoiceMessageLayout]; } self.cellData.cellLayout = layout; self.cellData.direction = MsgDirectionIncoming; self.cellData.showName = YES; self.cellData.showMessageModifyReplies = NO; self.cellData.showMessageTime = YES; } - (void)revertRootMsg { self.cellData.cellLayout = self.originCellLayout; self.cellData.direction = self.direction; self.cellData.showName = self.showName; self.cellData.showMessageModifyReplies = YES; self.cellData.showMessageTime = self.showMessageTime; } - (NSMutableArray *)transUIMsgFromIMMsg:(NSArray *)msgs { NSMutableArray *uiMsgs = [NSMutableArray array]; for (NSInteger k = 0; k < msgs.count; k++) { V2TIMMessage *msg = msgs[k]; TUIMessageCellData *data = [TUITextMessageCellData getCellData:msg]; TUIMessageCellLayout *layout = TUIMessageCellLayout.incommingMessageLayout; if ([data isKindOfClass:TUITextMessageCellData.class]) { layout = TUIMessageCellLayout.incommingTextMessageLayout; } data.cellLayout = layout; data.direction = MsgDirectionIncoming; data.showName = YES; if (data) { data.innerMessage = msg; [uiMsgs addObject:data]; } } NSArray *sortedArray = [uiMsgs sortedArrayUsingComparator:^NSComparisonResult(TUIMessageCellData *obj1, TUIMessageCellData *obj2) { if ([obj1.innerMessage.timestamp timeIntervalSince1970] == [obj2.innerMessage.timestamp timeIntervalSince1970]) { return obj1.innerMessage.seq > obj2.innerMessage.seq; } else { return [obj1.innerMessage.timestamp compare:obj2.innerMessage.timestamp]; } }]; uiMsgs = [NSMutableArray arrayWithArray:sortedArray]; return uiMsgs; } #pragma mark - tableView - (UITableView *)tableView { if (!_tableView) { CGRect rect = self.view.bounds; _tableView = [[UITableView alloc] initWithFrame:rect style:UITableViewStylePlain]; if (@available(iOS 15.0, *)) { _tableView.sectionHeaderTopPadding = 0; } [self.view addSubview:_tableView]; } return _tableView; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 2; } - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { if (section == 1) { return 20; } return 0; } - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { if (section == 1) { UIView *view = [[UIView alloc] init]; view.backgroundColor = [UIColor clearColor]; return view; } return nil; } - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { if (section == 0) { return 0.5; } return 0; } - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { if (section == 0) { UIView *line = [[UIView alloc] initWithFrame:CGRectMake(0, 0, Screen_Width, 0.5)]; line.backgroundColor = TIMCommonDynamicColor(@"separator_color", @"#DBDBDB"); return line; } return nil; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if (section == 0) { return 1; } return _uiMsgs.count; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.section == 0) { return [self.messageCellConfig getHeightFromMessageCellData:self.cellData]; } else { if (indexPath.row < self.uiMsgs.count) { TUIMessageCellData *cellData = self.uiMsgs[indexPath.row]; return [self.messageCellConfig getHeightFromMessageCellData:cellData]; } else { return 0; } } } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.section == 0) { TUIMessageCell *cell = nil; TUIMessageCellData *data = self.cellData; cell = [tableView dequeueReusableCellWithIdentifier:data.reuseId forIndexPath:indexPath]; cell.delegate = self; [cell fillWithData:data]; [cell notifyBottomContainerReadyOfData:nil]; return cell; } TUIMessageCellData *data = _uiMsgs[indexPath.row]; data.showMessageTime = YES; data.showCheckBox = NO; TUIMessageCell *cell = nil; cell = [tableView dequeueReusableCellWithIdentifier:data.reuseId forIndexPath:indexPath]; [cell fillWithData:_uiMsgs[indexPath.row]]; cell.delegate = self; [cell notifyBottomContainerReadyOfData:nil]; if ([cell isKindOfClass:TUIBubbleMessageCell.class]) { TUIBubbleMessageCell *bubbleCell = (TUIBubbleMessageCell *)cell; if (bubbleCell.bubbleView) { bubbleCell.bubbleView.image = nil; } } return cell; } - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { [self.inputController reset]; } #pragma mark - TUIInputControllerDelegate - (void)inputController:(TUIInputController *)inputController didChangeHeight:(CGFloat)height { if (!self.responseKeyboard) { return; } if (self.inputController.replyData == nil) { [self onRelyMessage:self.cellData]; } [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ CGRect msgFrame = self.tableView.frame; msgFrame.size.height = self.view.frame.size.height - height; self.tableView.frame = msgFrame; CGRect inputFrame = self.inputController.view.frame; inputFrame.origin.y = msgFrame.origin.y + msgFrame.size.height; inputFrame.size.height = height; self.inputController.view.frame = inputFrame; [self scrollToBottom:NO]; } completion:nil]; } - (void)inputController:(TUIInputController *)inputController didSendMessage:(V2TIMMessage *)msg { [self sendMessage:msg]; } - (void)inputController:(TUIInputController *)inputController didSelectMoreCell:(TUIInputMoreCell *)cell { cell.disableDefaultSelectAction = NO; if (cell.disableDefaultSelectAction) { return; } } - (void)sendMessage:(V2TIMMessage *)message { TUIMessageCellData *cellData = nil; if (!cellData) { cellData = [TUIMessageDataProvider getCellData:message]; } if (cellData) { cellData.innerMessage.needReadReceipt = self.isMsgNeedReadReceipt; [self sendUIMessage:cellData]; } } - (void)sendUIMessage:(TUIMessageCellData *)cellData { @weakify(self); [self.parentPageDataProvider sendUIMsg:cellData toConversation:self.conversationData willSendBlock:^(BOOL isReSend, TUIMessageCellData *_Nonnull dateUIMsg) { @strongify(self); int delay = 1; if ([cellData isKindOfClass:[TUIImageMessageCellData class]]) { delay = 0; } @weakify(self); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ @strongify(self); if (cellData.status == Msg_Status_Sending) { [self changeMsg:cellData status:Msg_Status_Sending_2]; } }); } SuccBlock:^{ @strongify(self); [self changeMsg:cellData status:Msg_Status_Succ]; [self scrollToBottom:YES]; } FailBlock:^(int code, NSString *desc) { @strongify(self); [TUITool makeToastError:code msg:desc]; [self changeMsg:cellData status:Msg_Status_Fail]; }]; } - (void)scrollToBottom:(BOOL)animated { if (self.uiMsgs.count > 0) { [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.uiMsgs.count - 1 inSection:1] atScrollPosition:UITableViewScrollPositionBottom animated:animated]; } } - (void)changeMsg:(TUIMessageCellData *)msg status:(TMsgStatus)status { msg.status = status; NSInteger index = [self.uiMsgs indexOfObject:msg]; if ([self.tableView numberOfRowsInSection:0] > index) { TUIMessageCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0]]; [cell fillWithData:msg]; } else { NSLog(@"lack of cell"); } [[NSNotificationCenter defaultCenter] postNotificationName:@"kTUINotifyMessageStatusChanged" object:nil userInfo:@{ @"msg" : msg, @"status" : [NSNumber numberWithUnsignedInteger:status], @"msgSender" : self, }]; } #pragma mark - Message reply - (void)onRelyMessage:(nonnull TUIMessageCellData *)data { NSString *desc = @""; desc = [self replyReferenceMessageDesc:data]; TUIReplyPreviewData *replyData = [[TUIReplyPreviewData alloc] init]; replyData.msgID = data.msgID; replyData.msgAbstract = desc; replyData.sender = data.senderName; replyData.type = (NSInteger)data.innerMessage.elemType; replyData.originMessage = data.innerMessage; self.inputController.replyData = replyData; } - (NSString *)replyReferenceMessageDesc:(TUIMessageCellData *)data { NSString *desc = @""; if (data.innerMessage.elemType == V2TIM_ELEM_TYPE_FILE) { desc = data.innerMessage.fileElem.filename; } else if (data.innerMessage.elemType == V2TIM_ELEM_TYPE_MERGER) { desc = data.innerMessage.mergerElem.title; } else if (data.innerMessage.elemType == V2TIM_ELEM_TYPE_CUSTOM) { desc = [TUIMessageDataProvider getDisplayString:data.innerMessage]; } else if (data.innerMessage.elemType == V2TIM_ELEM_TYPE_TEXT) { desc = data.innerMessage.textElem.text; } return desc; } #pragma mark - TUIMessageCellDelegate - (void)onSelectMessage:(TUIMessageCell *)cell { if (TUIChatConfig.defaultConfig.eventConfig.chatEventListener && [TUIChatConfig.defaultConfig.eventConfig.chatEventListener respondsToSelector:@selector(onMessageClicked:messageCellData:)]) { BOOL result = [TUIChatConfig.defaultConfig.eventConfig.chatEventListener onMessageClicked:cell messageCellData:cell.messageData]; if (result) { return; } } if ([cell isKindOfClass:[TUIImageMessageCell class]]) { [self showImageMessage:(TUIImageMessageCell *)cell]; } if ([cell isKindOfClass:[TUIVoiceMessageCell class]]) { [self playVoiceMessage:(TUIVoiceMessageCell *)cell]; } if ([cell isKindOfClass:[TUIVideoMessageCell class]]) { [self showVideoMessage:(TUIVideoMessageCell *)cell]; } if ([cell isKindOfClass:[TUIFileMessageCell class]]) { [self showFileMessage:(TUIFileMessageCell *)cell]; } if ([cell isKindOfClass:[TUIMergeMessageCell class]]) { TUIMergeMessageListController *mergeVc = [[TUIMergeMessageListController alloc] init]; mergeVc.mergerElem = [(TUIMergeMessageCell *)cell mergeData].mergerElem; mergeVc.delegate = self.delegate; [self.navigationController pushViewController:mergeVc animated:YES]; } if ([cell isKindOfClass:[TUILinkCell class]]) { [self showLinkMessage:(TUILinkCell *)cell]; } // if ([cell isKindOfClass:[TUIReplyMessageCell class]]) { // [self showReplyMessage:(TUIReplyMessageCell *)cell]; // } // if ([cell isKindOfClass:[TUIReferenceMessageCell class]]) { // [self showReplyMessage:(TUIReplyMessageCell *)cell]; // } if ([self.delegate respondsToSelector:@selector(messageController:onSelectMessageContent:)]) { [self.delegate messageController:nil onSelectMessageContent:cell]; } } #pragma mark - V2TIMAdvancedMsgListener - (void)onRecvNewMessage:(V2TIMMessage *)msg { V2TIMMessage *imMsg = msg; if (imMsg == nil || ![imMsg isKindOfClass:V2TIMMessage.class]) { return; } if ([imMsg.msgID isEqualToString:self.cellData.msgID] ) { TUIMessageCellData *cellData = [TUIMessageDataProvider getCellData:imMsg]; self.cellData.messageModifyReplies = cellData.messageModifyReplies; [self applyData]; } } - (void)onRecvMessageModified:(V2TIMMessage *)msg { V2TIMMessage *imMsg = msg; if (imMsg == nil || ![imMsg isKindOfClass:V2TIMMessage.class]) { return; } if ([imMsg.msgID isEqualToString:self.cellData.msgID] ) { TUIMessageCellData *cellData = [TUIMessageDataProvider getCellData:imMsg]; self.cellData.messageModifyReplies = cellData.messageModifyReplies; [self applyData]; } } #pragma mark - dataProviderDataChange - (void)dataProviderDataSourceWillChange:(TUIMessageDataProvider *)dataProvider { } - (void)dataProviderDataSourceChange:(TUIMessageDataProvider *)dataProvider withType:(TUIMessageBaseDataProviderDataSourceChangeType)type atIndex:(NSUInteger)index animation:(BOOL)animation { } - (void)dataProviderDataSourceDidChange:(TUIMessageDataProvider *)dataProvider { } - (void)dataProvider:(TUIMessageBaseDataProvider *)dataProvider onRemoveHeightCache:(TUIMessageCellData *)cellData { if (cellData) { [self.messageCellConfig removeHeightCacheOfMessageCellData:cellData]; } } #pragma mark - action - (void)showImageMessage:(TUIImageMessageCell *)cell { CGRect frame = [cell.thumb convertRect:cell.thumb.bounds toView:[UIApplication sharedApplication].delegate.window]; TUIMediaView *mediaView = [[TUIMediaView alloc] initWithFrame:CGRectMake(0, 0, Screen_Width, Screen_Height)]; [mediaView setThumb:cell.thumb frame:frame]; [mediaView setCurMessage:cell.messageData.innerMessage allMessages:@[ self.cellData.innerMessage ]]; [[UIApplication sharedApplication].keyWindow addSubview:mediaView]; } - (void)playVoiceMessage:(TUIVoiceMessageCell *)cell { TUIVoiceMessageCellData *uiMsg = (TUIVoiceMessageCellData *)self.cellData; if (uiMsg == cell.voiceData) { [uiMsg playVoiceMessage]; cell.voiceReadPoint.hidden = YES; } else { [uiMsg stopVoiceMessage]; } } - (void)showVideoMessage:(TUIVideoMessageCell *)cell { CGRect frame = [cell.thumb convertRect:cell.thumb.bounds toView:[UIApplication sharedApplication].delegate.window]; TUIMediaView *mediaView = [[TUIMediaView alloc] initWithFrame:CGRectMake(0, 0, Screen_Width, Screen_Height)]; [mediaView setThumb:cell.thumb frame:frame]; [mediaView setCurMessage:cell.messageData.innerMessage allMessages:@[ self.cellData.innerMessage ]]; __weak typeof(self) weakSelf = self; mediaView.onClose = ^{ [weakSelf.tableView reloadData]; }; [[UIApplication sharedApplication].keyWindow addSubview:mediaView]; } - (void)showFileMessage:(TUIFileMessageCell *)cell { TUIFileViewController *file = [[TUIFileViewController alloc] init]; file.data = [cell fileData]; [self.navigationController pushViewController:file animated:YES]; } - (void)showLinkMessage:(TUILinkCell *)cell { TUILinkCellData *cellData = cell.customData; if (cellData.link) { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:cellData.link]]; } } - (void)setConversation:(TUIChatConversationModel *)conversationData { self.conversationData = conversationData; if (!self.msgDataProvider) { self.msgDataProvider = [[TUIMessageDataProvider alloc] initWithConversationModel:conversationData]; self.msgDataProvider.dataSource = self; } [self loadMessage]; } - (void)loadMessage { if (self.msgDataProvider.isLoadingData || self.msgDataProvider.isNoMoreMsg) { return; } [self.msgDataProvider loadMessageSucceedBlock:^(BOOL isFirstLoad, BOOL isNoMoreMsg, NSArray *_Nonnull newMsgs) { } FailBlock:^(int code, NSString *desc) { [TUITool makeToastError:code msg:desc]; }]; } #pragma mark - TUINotificationProtocol - (void)onNotifyEvent:(NSString *)key subKey:(NSString *)subKey object:(id)anObject param:(NSDictionary *)param { if ([key isEqualToString:TUICore_TUIPluginNotify] && [subKey isEqualToString:TUICore_TUIPluginNotify_DidChangePluginViewSubKey]) { TUIMessageCellData *data = param[TUICore_TUIPluginNotify_DidChangePluginViewSubKey_Data]; NSInteger section = 1; if ([data.msgID isEqualToString:self.cellData.msgID] ) { //root section section = 0; } [self.messageCellConfig removeHeightCacheOfMessageCellData:data]; [self reloadAndScrollToBottomOfMessage:data.innerMessage.msgID section:section]; } } - (void)reloadAndScrollToBottomOfMessage:(NSString *)messageID section:(NSInteger)section { // Dispatch the task to RunLoop to ensure that they are executed after the UITableView refresh is complete. dispatch_async(dispatch_get_main_queue(), ^{ [self reloadCellOfMessage:messageID section:section]; dispatch_async(dispatch_get_main_queue(), ^{ [self scrollCellToBottomOfMessage:messageID section:section]; }); }); } - (void)reloadCellOfMessage:(NSString *)messageID section:(NSInteger)section { NSIndexPath *indexPath = [self indexPathOfMessage:messageID section:section]; // Disable animation when loading to avoid cell jumping. if (indexPath == nil) { return; } [UIView performWithoutAnimation:^{ dispatch_async(dispatch_get_main_queue(), ^{ [self.tableView reloadRowsAtIndexPaths:@[ indexPath ] withRowAnimation:UITableViewRowAnimationNone]; }); }]; } - (void)scrollCellToBottomOfMessage:(NSString *)messageID section:(NSInteger)section { NSIndexPath *indexPath = [self indexPathOfMessage:messageID section:section]; // Scroll the tableView only if the bottom of the cell is invisible. CGRect cellRect = [self.tableView rectForRowAtIndexPath:indexPath]; CGRect tableViewRect = self.tableView.bounds; BOOL isBottomInvisible = cellRect.origin.y < CGRectGetMaxY(tableViewRect) && CGRectGetMaxY(cellRect) > CGRectGetMaxY(tableViewRect); if (isBottomInvisible) { [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES]; } } - (NSIndexPath *)indexPathOfMessage:(NSString *)messageID section:(NSInteger)section { if (section == 0) { return [NSIndexPath indexPathForRow:0 inSection:section]; } else { for (int i = 0; i < self.uiMsgs.count; i++) { TUIMessageCellData *data = self.uiMsgs[i]; if ([data.innerMessage.msgID isEqualToString:messageID]) { return [NSIndexPath indexPathForRow:i inSection:section]; } } } return nil; } - (TUIMessageCellConfig *)messageCellConfig { if (_messageCellConfig == nil) { _messageCellConfig = [[TUIMessageCellConfig alloc] init]; } return _messageCellConfig; } @end