优化 XPRoomMessageContainerView 和 XPRoomMessageTableViewCell,改进消息处理逻辑,增加行高预计算方法,减少不必要的视图更新,提升性能和用户体验。同时,调整 UITableView 的约束更新方式,确保更高效的布局管理。
This commit is contained in:
@@ -108,27 +108,16 @@
|
|||||||
|
|
||||||
// 新增方法优化约束更新
|
// 新增方法优化约束更新
|
||||||
- (void)rebuildNormalConstraints {
|
- (void)rebuildNormalConstraints {
|
||||||
[self.contentLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
[self.contentLabel mas_updateConstraints:^(MASConstraintMaker *make) {
|
||||||
make.top.mas_equalTo(self.contentView);
|
make.top.mas_equalTo(self.contentView);
|
||||||
make.leading.mas_equalTo(self.contentView).offset(12);
|
make.leading.mas_equalTo(self.contentView).offset(12);
|
||||||
make.bottom.mas_equalTo(self.contentView).offset(-20);
|
make.bottom.mas_equalTo(self.contentView).offset(-20);
|
||||||
make.width.mas_equalTo(kRoomMessageMaxWidth);
|
make.width.mas_equalTo(kRoomMessageMaxWidth);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[self.bubbleImageView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
[self.bubbleImageView mas_updateConstraints:^(MASConstraintMaker *make) {
|
||||||
make.edges.mas_equalTo(self.contentLabel).insets(UIEdgeInsetsMake(0, -10, 0, -10));
|
make.edges.mas_equalTo(self.contentLabel).insets(UIEdgeInsetsMake(0, -10, 0, -10));
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
|
||||||
// [self.bubbleImageView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
|
||||||
// make.leading.mas_equalTo(8);
|
|
||||||
// make.top.bottom.mas_equalTo(0);
|
|
||||||
// make.trailing.mas_equalTo(-8);
|
|
||||||
// }];
|
|
||||||
//
|
|
||||||
// [self.contentLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
|
||||||
// make.edges.mas_equalTo(UIEdgeInsetsMake(4, 12, 4, 12));
|
|
||||||
// }];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - tool
|
#pragma mark - tool
|
||||||
@@ -164,20 +153,28 @@
|
|||||||
|
|
||||||
#pragma mark - Getters And Setters
|
#pragma mark - Getters And Setters
|
||||||
- (void)setMessageInfo:(XPMessageInfoModel *)messageInfo {
|
- (void)setMessageInfo:(XPMessageInfoModel *)messageInfo {
|
||||||
if ([messageInfo.content isEqualToAttributedString:_messageInfo.content]) {
|
// 更严格的比较,减少不必要的更新
|
||||||
|
if (_messageInfo &&
|
||||||
|
[messageInfo.content isEqualToAttributedString:_messageInfo.content] &&
|
||||||
|
[messageInfo.bubbleImageUrl isEqualToString:_messageInfo.bubbleImageUrl] &&
|
||||||
|
[messageInfo.boomImageUrl isEqualToString:_messageInfo.boomImageUrl]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_messageInfo = messageInfo;
|
_messageInfo = messageInfo;
|
||||||
if (messageInfo) {
|
if (messageInfo) {
|
||||||
// 确保在设置attributedText之前先设置hasBubble属性
|
// 确保在设置attributedText之前先设置hasBubble属性
|
||||||
self.contentLabel.hasBubble = ![NSString isEmpty:messageInfo.bubbleImageUrl];
|
BOOL hasBubble = ![NSString isEmpty:messageInfo.bubbleImageUrl];
|
||||||
|
if (self.contentLabel.hasBubble != hasBubble) {
|
||||||
|
self.contentLabel.hasBubble = hasBubble;
|
||||||
|
}
|
||||||
|
|
||||||
self.contentLabel.attributedText = messageInfo.content;
|
self.contentLabel.attributedText = messageInfo.content;
|
||||||
|
|
||||||
if (self.isLeftBigImage) {
|
if (self.isLeftBigImage && messageInfo.boomImageUrl) {
|
||||||
self.leftBigImageView.imageUrl = messageInfo.boomImageUrl;
|
self.leftBigImageView.imageUrl = messageInfo.boomImageUrl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateLayoutWithoutBubble:(BOOL)hasBubble layoutSize:(CGSize)size {
|
- (void)updateLayoutWithoutBubble:(BOOL)hasBubble layoutSize:(CGSize)size {
|
||||||
@@ -216,12 +213,12 @@
|
|||||||
UIImage *cutImage = [image1 cropRightAndBottomPixels:2];
|
UIImage *cutImage = [image1 cropRightAndBottomPixels:2];
|
||||||
self.bubbleImageView.image = [self resizableImage:cutImage];
|
self.bubbleImageView.image = [self resizableImage:cutImage];
|
||||||
}];
|
}];
|
||||||
[self.bubbleImageView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
[self.bubbleImageView mas_updateConstraints:^(MASConstraintMaker *make) {
|
||||||
make.edges.mas_equalTo(self.contentLabel).insets(UIEdgeInsetsMake(-10, -10, -10, -10));
|
make.edges.mas_equalTo(self.contentLabel).insets(UIEdgeInsetsMake(-10, -10, -10, -10));
|
||||||
}];
|
}];
|
||||||
} else {
|
} else {
|
||||||
self.bubbleImageView.image = [UIImage imageWithColor:[DJDKMIMOMColor messageBubbleColor]];
|
self.bubbleImageView.image = [UIImage imageWithColor:[DJDKMIMOMColor messageBubbleColor]];
|
||||||
[self.bubbleImageView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
[self.bubbleImageView mas_updateConstraints:^(MASConstraintMaker *make) {
|
||||||
make.edges.mas_equalTo(self.contentLabel).insets(UIEdgeInsetsMake(0, -10, 0, -10));
|
make.edges.mas_equalTo(self.contentLabel).insets(UIEdgeInsetsMake(0, -10, 0, -10));
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
@@ -210,9 +210,11 @@ NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. 超长消息处理逻辑
|
// 4. 超长消息处理逻辑
|
||||||
|
BOOL needReloadData = NO;
|
||||||
if (self.datasource.count > kRoomMessageMaxLength) {
|
if (self.datasource.count > kRoomMessageMaxLength) {
|
||||||
NSInteger removedCount = kRoomMessageMaxLength / 2;
|
NSInteger removedCount = kRoomMessageMaxLength / 2;
|
||||||
[self safelyRemoveMessages:removedCount];
|
[self safelyRemoveMessages:removedCount];
|
||||||
|
needReloadData = YES; // 标记需要重新加载数据
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 插入新消息
|
// 5. 插入新消息
|
||||||
@@ -230,7 +232,13 @@ NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
|
|||||||
}
|
}
|
||||||
[self updateAllDataSource:tempNewDatas];
|
[self updateAllDataSource:tempNewDatas];
|
||||||
[self.incomingMessages removeAllObjects];
|
[self.incomingMessages removeAllObjects];
|
||||||
[self.messageTableView reloadData];
|
|
||||||
|
// 如果有删除操作,使用 reloadData;否则使用增量更新
|
||||||
|
if (needReloadData) {
|
||||||
|
[self.messageTableView reloadData];
|
||||||
|
} else if (indexPaths.count > 0) {
|
||||||
|
[self.messageTableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
|
||||||
|
}
|
||||||
|
|
||||||
// 6. 滚动到指定位置或底部
|
// 6. 滚动到指定位置或底部
|
||||||
[self scrollToFirstLocationOrBottom];
|
[self scrollToFirstLocationOrBottom];
|
||||||
@@ -463,13 +471,16 @@ NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOL needReloadData = NO;
|
||||||
if (self.datasource.count > kRoomMessageMaxLength) {
|
if (self.datasource.count > kRoomMessageMaxLength) {
|
||||||
NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, kRoomMessageMaxLength/2)];
|
NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, kRoomMessageMaxLength/2)];
|
||||||
NSArray *needRemoveMsgArray = [self.datasource objectsAtIndexes:set];
|
NSArray *needRemoveMsgArray = [self.datasource objectsAtIndexes:set];
|
||||||
[self.datasource removeObjectsInArray:needRemoveMsgArray];
|
[self.datasource removeObjectsInArray:needRemoveMsgArray];
|
||||||
|
needReloadData = YES; // 标记需要重新加载数据
|
||||||
}
|
}
|
||||||
|
|
||||||
NSMutableArray *tempArray = @[].mutableCopy;
|
NSMutableArray *tempArray = @[].mutableCopy;
|
||||||
|
NSMutableArray *indexPaths = @[].mutableCopy;
|
||||||
@kWeakify(self);
|
@kWeakify(self);
|
||||||
[self.incomingMessages enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
[self.incomingMessages enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||||
@kStrongify(self);
|
@kStrongify(self);
|
||||||
@@ -482,13 +493,20 @@ NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
|
|||||||
if (model) {
|
if (model) {
|
||||||
[tempArray addObject:model];
|
[tempArray addObject:model];
|
||||||
[self.datasource addObject:model];
|
[self.datasource addObject:model];
|
||||||
|
[indexPaths addObject:[NSIndexPath indexPathForRow:self.datasource.count - 1 inSection:0]];
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[self.incomingMessages removeAllObjects];
|
[self.incomingMessages removeAllObjects];
|
||||||
|
|
||||||
[self updateAllDataSource:tempArray];
|
[self updateAllDataSource:tempArray];
|
||||||
[self.messageTableView reloadData];
|
|
||||||
|
// 如果有删除操作,使用 reloadData;否则使用增量更新
|
||||||
|
if (needReloadData) {
|
||||||
|
[self.messageTableView reloadData];
|
||||||
|
} else if (indexPaths.count > 0) {
|
||||||
|
[self.messageTableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
|
||||||
|
}
|
||||||
|
|
||||||
//执行插入动画并滚动
|
//执行插入动画并滚动
|
||||||
[self scrollToBottom:NO];
|
[self scrollToBottom:NO];
|
||||||
@@ -514,7 +532,17 @@ NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
|
|||||||
}
|
}
|
||||||
if(source.count > 0){
|
if(source.count > 0){
|
||||||
NSIndexPath *ip = [NSIndexPath indexPathForRow:source.count-1 inSection:0]; //取最后一行数据
|
NSIndexPath *ip = [NSIndexPath indexPathForRow:source.count-1 inSection:0]; //取最后一行数据
|
||||||
[self.messageTableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionBottom animated:animated]; //滚动到最后一行
|
|
||||||
|
// 优化滚动动画,减少与布局更新的冲突
|
||||||
|
if (animated) {
|
||||||
|
// 使用 dispatch_async 确保布局更新完成后再滚动
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self.messageTableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionBottom animated:YES];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
[self.messageTableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionBottom animated:NO];
|
||||||
|
}
|
||||||
|
|
||||||
self.atCount = 0;
|
self.atCount = 0;
|
||||||
self.atTipBtn.hidden = YES;
|
self.atTipBtn.hidden = YES;
|
||||||
[self.locationArray removeAllObjects];
|
[self.locationArray removeAllObjects];
|
||||||
@@ -1282,14 +1310,33 @@ NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
|
|||||||
|
|
||||||
XPMessageInfoModel *model = [source xpSafeObjectAtIndex:indexPath.row];
|
XPMessageInfoModel *model = [source xpSafeObjectAtIndex:indexPath.row];
|
||||||
|
|
||||||
// 如果 model 中有高度数据,使用 model 的高度
|
// 预计算并缓存高度,避免使用 UITableViewAutomaticDimension
|
||||||
if (model.rowHeight > 0) {
|
if (model.rowHeight <= 0) {
|
||||||
return model.rowHeight + 20;
|
[self calculateRowHeightForModel:model];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 否则,使用自适应高度
|
return model.rowHeight + 20;
|
||||||
// return 100;
|
}
|
||||||
return UITableViewAutomaticDimension;
|
|
||||||
|
// 新增方法:预计算行高
|
||||||
|
- (void)calculateRowHeightForModel:(XPMessageInfoModel *)model {
|
||||||
|
if (!model || !model.content) {
|
||||||
|
model.rowHeight = 44; // 默认高度
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 YYTextLayout 计算文本高度
|
||||||
|
YYTextContainer *container = [YYTextContainer new];
|
||||||
|
container.size = CGSizeMake(kRoomMessageMaxWidth - 24, MAXFLOAT);
|
||||||
|
container.maximumNumberOfRows = 0;
|
||||||
|
|
||||||
|
YYTextLayout *layout = [YYTextLayout layoutWithContainer:container text:model.content];
|
||||||
|
model.rowHeight = layout.textBoundingSize.height;
|
||||||
|
|
||||||
|
// 确保最小高度
|
||||||
|
if (model.rowHeight < 44) {
|
||||||
|
model.rowHeight = 44;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||||
@@ -1492,8 +1539,8 @@ NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
|
|||||||
_messageTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
|
_messageTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
|
||||||
_messageTableView.delegate = self;
|
_messageTableView.delegate = self;
|
||||||
_messageTableView.dataSource = self;
|
_messageTableView.dataSource = self;
|
||||||
_messageTableView.rowHeight = UITableViewAutomaticDimension;
|
// 移除 UITableViewAutomaticDimension,使用预计算的高度
|
||||||
// _messageTableView.estimatedRowHeight = 44; // 预估高度
|
_messageTableView.rowHeight = 64; // 设置一个合理的默认高度
|
||||||
_messageTableView.tableFooterView = [UIView new];
|
_messageTableView.tableFooterView = [UIView new];
|
||||||
_messageTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
_messageTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||||
_messageTableView.backgroundColor = [UIColor clearColor];
|
_messageTableView.backgroundColor = [UIColor clearColor];
|
||||||
|
Reference in New Issue
Block a user