// // TUIMergeMessageListController.m // Pods // // Created by harvy on 2020/12/9. // Copyright © 2023 Tencent. All rights reserved. // #import "TUIMergeMessageListController.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 "TUIMessageDataProvider.h" #import "TUIMessageSearchDataProvider.h" #import "TUIReferenceMessageCell.h" #import "TUIRepliesDetailViewController.h" #import "TUIReplyMessageCell.h" #import "TUIReplyMessageCellData.h" #import "TUITextMessageCell.h" #import "TUIVideoMessageCell.h" #import "TUIVoiceMessageCell.h" #import "TUIMessageCellConfig.h" #import "TUIChatConfig.h" #define STR(x) @ #x @interface TUIMergeMessageListController () @property(nonatomic, strong) NSArray *imMsgs; @property(nonatomic, strong) NSMutableArray *uiMsgs; @property(nonatomic, strong) NSMutableDictionary *stylesCache; @property(nonatomic, strong) TUIMessageSearchDataProvider *msgDataProvider; @property(nonatomic, strong) TUIMessageCellConfig *messageCellConfig; @end @implementation TUIMergeMessageListController - (instancetype)init { self = [super init]; if (self) { [TUICore registerEvent:TUICore_TUIPluginNotify subKey:TUICore_TUIPluginNotify_DidChangePluginViewSubKey object:self]; } return self; } - (void)viewDidLoad { [super viewDidLoad]; _uiMsgs = [[NSMutableArray alloc] init]; [self loadMessages]; [self setupViews]; } - (void)viewWillDisappear:(BOOL)animated { [self updateCellStyle:YES]; if (self.willCloseCallback) { self.willCloseCallback(); } } - (void)updateCellStyle:(BOOL)recover { if (recover) { TUIMessageCellLayout.incommingMessageLayout.avatarInsets = UIEdgeInsetsFromString(self.stylesCache[STR(incomingAvatarInsets)]); TUIMessageCellLayout.incommingTextMessageLayout.avatarInsets = UIEdgeInsetsFromString(self.stylesCache[STR(incomingAvatarInsets)]); TUIMessageCellLayout.incommingVoiceMessageLayout.avatarInsets = UIEdgeInsetsFromString(self.stylesCache[STR(incomingAvatarInsets)]); TUIMessageCellLayout.incommingMessageLayout.messageInsets = UIEdgeInsetsFromString(self.stylesCache[STR(incommingMessageInsets)]); [TUITextMessageCell setOutgoingTextColor:self.stylesCache[STR(outgoingTextColor)]]; [TUITextMessageCell setIncommingTextColor:self.stylesCache[STR(incomingTextColor)]]; return; } UIEdgeInsets incomingAvatarInsets = TUIMessageCellLayout.incommingTextMessageLayout.avatarInsets; TUIMessageCellLayout.incommingMessageLayout.avatarInsets = UIEdgeInsetsMake(0, 10, 0, 10); TUIMessageCellLayout.incommingTextMessageLayout.avatarInsets = UIEdgeInsetsMake(0, 10, 0, 10); TUIMessageCellLayout.incommingVoiceMessageLayout.avatarInsets = UIEdgeInsetsMake(0, 10, 0, 10); [self.stylesCache setObject:NSStringFromUIEdgeInsets(incomingAvatarInsets) forKey:STR(incomingAvatarInsets)]; UIEdgeInsets incommingMessageInsets = TUIMessageCellLayout.incommingMessageLayout.messageInsets; TUIMessageCellLayout.incommingMessageLayout.messageInsets = UIEdgeInsetsMake(5, 5, 0, 0); [self.stylesCache setObject:NSStringFromUIEdgeInsets(incommingMessageInsets) forKey:STR(incommingMessageInsets)]; UIColor *outgoingTextColor = [TUITextMessageCell outgoingTextColor]; [TUITextMessageCell setOutgoingTextColor:TUIChatDynamicColor(@"chat_text_message_send_text_color", @"#000000")]; [self.stylesCache setObject:outgoingTextColor forKey:STR(outgoingTextColor)]; UIColor *incomingTextColor = [TUITextMessageCell incommingTextColor]; [TUITextMessageCell setIncommingTextColor:TUIChatDynamicColor(@"chat_text_message_receive_text_color", @"#000000")]; [self.stylesCache setObject:incomingTextColor forKey:STR(incomingTextColor)]; } - (void)loadMessages { @weakify(self); [self.mergerElem downloadMergerMessage:^(NSArray *msgs) { @strongify(self); self.imMsgs = msgs; [self updateCellStyle:NO]; [self getMessages:self.imMsgs]; } fail:^(int code, NSString *desc) { [self updateCellStyle:NO]; }]; } - (void)getMessages:(NSArray *)msgs { NSMutableArray *uiMsgs = [self transUIMsgFromIMMsg:msgs]; @weakify(self); [self.msgDataProvider preProcessMessage:uiMsgs callback:^{ @strongify(self); @weakify(self); dispatch_async(dispatch_get_main_queue(), ^{ @strongify(self); if (uiMsgs.count != 0) { NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, uiMsgs.count)]; [self.uiMsgs insertObjects:uiMsgs atIndexes:indexSet]; [self.tableView reloadData]; [self.tableView layoutIfNeeded]; } }); }]; } - (NSMutableArray *)transUIMsgFromIMMsg:(NSArray *)msgs { NSMutableArray *uiMsgs = [NSMutableArray array]; for (NSInteger k = 0; k < msgs.count; k++) { V2TIMMessage *msg = msgs[k]; if ([self.delegate respondsToSelector:@selector(messageController:onNewMessage:)]) { TUIMessageCellData *data = [self.delegate messageController:nil onNewMessage:msg]; if (data) { TUIMessageCellLayout *layout = TUIMessageCellLayout.incommingMessageLayout; if ([data isKindOfClass:TUITextMessageCellData.class] || [data isKindOfClass:TUIReferenceMessageCellData.class]) { layout = TUIMessageCellLayout.incommingTextMessageLayout; } else if ([data isKindOfClass:TUIVoiceMessageCellData.class]) { layout = TUIMessageCellLayout.incommingVoiceMessageLayout; } data.cellLayout = layout; data.direction = MsgDirectionIncoming; data.innerMessage = msg; data.showName = YES; [uiMsgs addObject:data]; continue; } } TUIMessageCellData *data = [TUIMessageDataProvider getCellData:msg]; if (!data) { continue; } TUIMessageCellLayout *layout = TUIMessageCellLayout.incommingMessageLayout; if ([data isKindOfClass:TUITextMessageCellData.class]) { layout = TUIMessageCellLayout.incommingTextMessageLayout; } else if ([data isKindOfClass:TUIReplyMessageCellData.class] || [data isKindOfClass:TUIReferenceMessageCellData.class]) { layout = TUIMessageCellLayout.incommingTextMessageLayout; TUIReferenceMessageCellData *textData = (TUIReferenceMessageCellData *)data; textData.textColor = TUIChatDynamicColor(@"chat_text_message_receive_text_color", @"#000000"); textData.showRevokedOriginMessage = YES; } else if ([data isKindOfClass:TUIVoiceMessageCellData.class]) { TUIVoiceMessageCellData *voiceData = (TUIVoiceMessageCellData *)data; voiceData.cellLayout = [TUIMessageCellLayout incommingVoiceMessageLayout]; voiceData.voiceImage = [[TUIImageCache sharedInstance] getResourceFromCache:TUIChatImagePath(@"message_voice_receiver_normal")]; voiceData.voiceAnimationImages = [NSArray arrayWithObjects:[[TUIImageCache sharedInstance] getResourceFromCache:TUIChatImagePath(@"message_voice_receiver_playing_1")], [[TUIImageCache sharedInstance] getResourceFromCache:TUIChatImagePath(@"message_voice_receiver_playing_2")], [[TUIImageCache sharedInstance] getResourceFromCache:TUIChatImagePath(@"message_voice_receiver_playing_3")], nil]; voiceData.voiceTop = 10; msg.localCustomInt = 1; layout = TUIMessageCellLayout.incommingVoiceMessageLayout; } data.cellLayout = layout; data.direction = MsgDirectionIncoming; data.innerMessage = msg; data.showName = YES; [uiMsgs addObject:data]; } return uiMsgs; } - (void)setupViews { self.title = TIMCommonLocalizableString(TUIKitRelayChatHistory); self.tableView.scrollsToTop = NO; self.tableView.estimatedRowHeight = 0; [self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone]; self.tableView.backgroundColor = TUIChatDynamicColor(@"chat_controller_bg_color", @"#FFFFFF"); self.tableView.contentInset = UIEdgeInsetsMake(5, 0, 0, 0); [self.messageCellConfig bindTableView:self.tableView]; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return _uiMsgs.count; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { static CGFloat screenWidth = 0; if (screenWidth == 0) { screenWidth = Screen_Width; } if (indexPath.row < _uiMsgs.count) { TUIMessageCellData *cellData = _uiMsgs[indexPath.row]; CGFloat height = [self.messageCellConfig getHeightFromMessageCellData:cellData]; return height; } else { return 0; } } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { TUIMessageCellData *data = _uiMsgs[indexPath.row]; data.showMessageTime = YES; data.showCheckBox = NO; TUIMessageCell *cell = nil; if ([self.delegate respondsToSelector:@selector(messageController:onShowMessageData:)]) { cell = [self.delegate messageController:nil onShowMessageData:data]; if (cell) { cell.delegate = self; return cell; } } cell = [tableView dequeueReusableCellWithIdentifier:data.reuseId forIndexPath:indexPath]; cell.delegate = self; [cell fillWithData:_uiMsgs[indexPath.row]]; return cell; } #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]; } } - (BOOL)checkIfMessageExistsInLocal:(V2TIMMessage *)locateMessage { NSInteger index = 0; for (TUIMessageCellData *uiMsg in self.uiMsgs) { if ([uiMsg.innerMessage.msgID isEqualToString:locateMessage.msgID]) { return YES; } index++; } if (index == self.uiMsgs.count) { return NO; } return NO; } - (void)scrollToLocateMessage:(V2TIMMessage *)locateMessage matchKeyword:(NSString *)msgAbstract { CGFloat offsetY = 0; NSInteger index = 0; for (TUIMessageCellData *uiMsg in self.uiMsgs) { if ([uiMsg.innerMessage.msgID isEqualToString:locateMessage.msgID]) { break; } offsetY += [uiMsg heightOfWidth:Screen_Width]; index++; } if (index == self.uiMsgs.count) { return; } offsetY -= self.tableView.frame.size.height / 2.0; if (offsetY <= TMessageController_Header_Height) { offsetY = TMessageController_Header_Height + 0.1; } if (offsetY > TMessageController_Header_Height) { [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES]; } [self highlightKeyword:msgAbstract locateMessage:locateMessage]; } - (void)highlightKeyword:(NSString *)keyword locateMessage:(V2TIMMessage *)locateMessage { TUIMessageCellData *cellData = nil; for (TUIMessageCellData *tmp in self.uiMsgs) { if ([tmp.msgID isEqualToString:locateMessage.msgID]) { cellData = tmp; break; } } if (cellData == nil) { return; } CGFloat time = 0.5; UITableViewRowAnimation animation = UITableViewRowAnimationFade; if ([cellData isKindOfClass:TUITextMessageCellData.class]) { time = 2; animation = UITableViewRowAnimationNone; } @weakify(self); dispatch_async(dispatch_get_main_queue(), ^{ @strongify(self); NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[self.uiMsgs indexOfObject:cellData] inSection:0]; cellData.highlightKeyword = keyword; TUIMessageCell *cell = [self.tableView cellForRowAtIndexPath:indexPath]; [cell fillWithData:cellData]; }); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(time * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ @strongify(self); NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[self.uiMsgs indexOfObject:cellData] inSection:0]; cellData.highlightKeyword = nil; TUIMessageCell *cell = [self.tableView cellForRowAtIndexPath:indexPath]; [cell fillWithData:cellData]; }); } - (void)showReplyMessage:(TUIReplyMessageCell *)cell { [UIApplication.sharedApplication.keyWindow endEditing:YES]; NSString *originMsgID = @""; NSString *msgAbstract = @""; if ([cell isKindOfClass:TUIReplyMessageCell.class]) { TUIReplyMessageCell *acell = (TUIReplyMessageCell *)cell; TUIReplyMessageCellData *cellData = acell.replyData; originMsgID = cellData.messageRootID; msgAbstract = cellData.msgAbstract; } else if ([cell isKindOfClass:TUIReferenceMessageCell.class]) { TUIReferenceMessageCell *acell = (TUIReferenceMessageCell *)cell; TUIReferenceMessageCellData *cellData = acell.referenceData; originMsgID = cellData.originMsgID; msgAbstract = cellData.msgAbstract; } TUIMessageCellData *originMemoryMessageData = nil; for (TUIMessageCellData *uiMsg in self.uiMsgs) { if ([uiMsg.innerMessage.msgID isEqualToString:originMsgID]) { originMemoryMessageData = uiMsg; break; } } if (originMemoryMessageData && [cell isKindOfClass:TUIReplyMessageCell.class]) { [self onJumpToRepliesDetailPage:originMemoryMessageData]; } else { [(TUIMessageSearchDataProvider *)self.msgDataProvider findMessages:@[ originMsgID ?: @"" ] callback:^(BOOL success, NSString *_Nonnull desc, NSArray *_Nonnull msgs) { if (!success) { [TUITool makeToast:TIMCommonLocalizableString(TUIKitReplyMessageNotFoundOriginMessage)]; return; } V2TIMMessage *message = msgs.firstObject; if (message == nil) { [TUITool makeToast:TIMCommonLocalizableString(TUIKitReplyMessageNotFoundOriginMessage)]; return; } if (message.status == V2TIM_MSG_STATUS_HAS_DELETED || message.status == V2TIM_MSG_STATUS_LOCAL_REVOKED) { [TUITool makeToast:TIMCommonLocalizableString(TUIKitReplyMessageNotFoundOriginMessage)]; return; } if ([cell isKindOfClass:TUIReplyMessageCell.class]) { [self jumpDetailPageByMessage:message]; } else if ([cell isKindOfClass:TUIReferenceMessageCell.class]) { BOOL existInLocal = [self checkIfMessageExistsInLocal:message]; if (!existInLocal) { [TUITool makeToast:TIMCommonLocalizableString(TUIKitReplyMessageNotFoundOriginMessage)]; return; } [self scrollToLocateMessage:message matchKeyword:msgAbstract]; } }]; } } - (void)jumpDetailPageByMessage:(V2TIMMessage *)message { NSMutableArray *uiMsgs = [self transUIMsgFromIMMsg:@[ message ]]; [self.msgDataProvider preProcessMessage:uiMsgs callback:^{ for (TUIMessageCellData *cellData in uiMsgs) { if ([cellData.innerMessage.msgID isEqual:message.msgID]) { [self onJumpToRepliesDetailPage:cellData]; return; } } }]; } - (void)onJumpToRepliesDetailPage:(TUIMessageCellData *)data { TUIRepliesDetailViewController *repliesDetailVC = [[TUIRepliesDetailViewController alloc] initWithCellData:data conversationData:self.conversationData]; repliesDetailVC.delegate = self.delegate; [self.navigationController pushViewController:repliesDetailVC animated:YES]; repliesDetailVC.parentPageDataProvider = self.parentPageDataProvider; __weak typeof(self) weakSelf = self; repliesDetailVC.willCloseCallback = ^() { [weakSelf.tableView reloadData]; }; } - (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.imMsgs]; [[UIApplication sharedApplication].keyWindow addSubview:mediaView]; } - (void)playVoiceMessage:(TUIVoiceMessageCell *)cell { for (NSInteger index = 0; index < _uiMsgs.count; ++index) { if (![_uiMsgs[index] isKindOfClass:[TUIVoiceMessageCellData class]]) { continue; } TUIVoiceMessageCellData *uiMsg = (TUIVoiceMessageCellData *)_uiMsgs[index]; 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.imMsgs]; [[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]]; } } - (NSMutableDictionary *)stylesCache { if (_stylesCache == nil) { _stylesCache = [NSMutableDictionary dictionary]; } return _stylesCache; } - (UIImage *)bubbleImage { CGRect rect = CGRectMake(0, 0, 100, 40); UIGraphicsBeginImageContext(rect.size); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(context, [[UIColor clearColor] CGColor]); CGContextFillRect(context, rect); UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return img; } - (TUIMessageSearchDataProvider *)msgDataProvider { if (_msgDataProvider == nil) { _msgDataProvider = [[TUIMessageSearchDataProvider alloc] init]; _msgDataProvider.dataSource = self; } return _msgDataProvider; } - (TUIMessageCellConfig *)messageCellConfig { if (_messageCellConfig == nil) { _messageCellConfig = [[TUIMessageCellConfig alloc] init]; } return _messageCellConfig; } #pragma mark - TUIMessageBaseDataProviderDataSource - (void)dataProviderDataSourceWillChange:(TUIMessageBaseDataProvider *)dataProvider { // do nothing } - (void)dataProviderDataSourceChange:(TUIMessageBaseDataProvider *)dataProvider withType:(TUIMessageBaseDataProviderDataSourceChangeType)type atIndex:(NSUInteger)index animation:(BOOL)animation { // do nothing } - (void)dataProviderDataSourceDidChange:(TUIMessageBaseDataProvider *)dataProvider { [self.tableView reloadData]; } - (void)dataProvider:(TUIMessageBaseDataProvider *)dataProvider onRemoveHeightCache:(TUIMessageCellData *)cellData { if (cellData) { [self.messageCellConfig removeHeightCacheOfMessageCellData:cellData]; } } #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]; [self.messageCellConfig removeHeightCacheOfMessageCellData:data]; [self reloadAndScrollToBottomOfMessage:data.innerMessage.msgID section:0]; } } - (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 { 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; } @end