修复聊天最后一条跑到最上面的问题

This commit is contained in:
2026-01-29 13:13:42 +08:00
parent ef52cd4872
commit 4392296616
3 changed files with 126 additions and 59 deletions

View File

@@ -90,10 +90,13 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
make.edges.equalTo(self); make.edges.equalTo(self);
}]; }];
// contentBottomInset chatView // contentInset
self.contentBottomInset = 20; // self.contentBottomInset = 0;
[self updateContentBottomInset:self.contentBottomInset]; [self updateContentBottomInset:self.contentBottomInset];
// mj_footer
// TODO:
/*
__weak typeof(self) weakSelf = self; __weak typeof(self) weakSelf = self;
self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{ self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
__strong typeof(weakSelf) strongSelf = weakSelf; __strong typeof(weakSelf) strongSelf = weakSelf;
@@ -114,11 +117,12 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
}]; }];
// "已经全部加载完毕" // "已经全部加载完毕"
MJRefreshAutoNormalFooter *footer = (MJRefreshAutoNormalFooter *)self.tableView.mj_footer; // MJRefreshAutoNormalFooter *footer = (MJRefreshAutoNormalFooter *)self.tableView.mj_footer;
footer.stateLabel.hidden = YES; // // footer.stateLabel.hidden = YES; //
footer.refreshingBlock = footer.refreshingBlock; // // footer.refreshingBlock = footer.refreshingBlock; //
self.tableView.mj_footer.hidden = YES; // self.tableView.mj_footer.hidden = YES;
*/
} }
#pragma mark - Public Methods #pragma mark - Public Methods
@@ -206,39 +210,56 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
// 使 layoutIfNeeded // 使 layoutIfNeeded
[self.tableView layoutIfNeeded]; [self.tableView layoutIfNeeded];
NSIndexPath *lastIndexPath = [NSIndexPath indexPathForRow:self.messages.count - 1 //
inSection:0]; CGFloat contentHeight = self.tableView.contentSize.height;
CGFloat tableViewHeight = self.tableView.bounds.size.height;
CGFloat bottomInset = self.tableView.contentInset.bottom;
// // tableView
[self.tableView scrollToRowAtIndexPath:lastIndexPath if (contentHeight <= tableViewHeight) {
atScrollPosition:UITableViewScrollPositionBottom NSLog(@"[KBChatTableView] 内容高度(%.2f) <= tableView高度(%.2f),不需要滚动", contentHeight, tableViewHeight);
animated:animated]; return;
}
// offset
CGFloat offsetY = contentHeight - tableViewHeight + bottomInset;
NSLog(@"[KBChatTableView] scrollToBottom - contentHeight: %.2f, tableViewHeight: %.2f, bottomInset: %.2f, offsetY: %.2f",
contentHeight, tableViewHeight, bottomInset, offsetY);
[self.tableView setContentOffset:CGPointMake(0, offsetY) animated:animated];
} }
#pragma mark - Public Helpers #pragma mark - Public Helpers
- (void)endLoadMoreWithHasMoreData:(BOOL)hasMoreData { - (void)endLoadMoreWithHasMoreData:(BOOL)hasMoreData {
self.hasMoreData = hasMoreData; self.hasMoreData = hasMoreData;
if (hasMoreData) { // mj_footer
[self.tableView.mj_footer endRefreshing]; // if (hasMoreData) {
} else { // [self.tableView.mj_footer endRefreshing];
[self.tableView.mj_footer endRefreshingWithNoMoreData]; // } else {
} // [self.tableView.mj_footer endRefreshingWithNoMoreData];
// }
[self updateFooterVisibility]; [self updateFooterVisibility];
} }
- (void)resetNoMoreData { - (void)resetNoMoreData {
self.hasMoreData = YES; self.hasMoreData = YES;
[self.tableView.mj_footer resetNoMoreData]; // mj_footer
// [self.tableView.mj_footer resetNoMoreData];
[self updateFooterVisibility]; [self updateFooterVisibility];
} }
- (void)updateContentBottomInset:(CGFloat)bottomInset { - (void)updateContentBottomInset:(CGFloat)bottomInset {
self.contentBottomInset = bottomInset; self.contentBottomInset = bottomInset;
UIEdgeInsets insets = self.tableView.contentInset;
// contentInset
UIEdgeInsets insets = UIEdgeInsetsZero;
insets.bottom = bottomInset; insets.bottom = bottomInset;
self.tableView.contentInset = insets; self.tableView.contentInset = insets;
self.tableView.scrollIndicatorInsets = insets; self.tableView.scrollIndicatorInsets = insets;
NSLog(@"[KBChatTableView] updateContentBottomInset: %.2f", bottomInset);
} }
- (void)addMessage:(KBAiChatMessage *)message - (void)addMessage:(KBAiChatMessage *)message
@@ -296,10 +317,32 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
} }
} }
NSLog(@"[KBChatTableView] ========== reloadWithMessages 开始 ==========");
NSLog(@"[KBChatTableView] 消息数量: %ld", (long)self.messages.count);
NSLog(@"[KBChatTableView] tableView.frame: %@", NSStringFromCGRect(self.tableView.frame));
NSLog(@"[KBChatTableView] tableView.bounds: %@", NSStringFromCGRect(self.tableView.bounds));
NSLog(@"[KBChatTableView] 刷新前 contentSize: %@", NSStringFromCGSize(self.tableView.contentSize));
NSLog(@"[KBChatTableView] 刷新前 contentInset: %@", NSStringFromUIEdgeInsets(self.tableView.contentInset));
[self.tableView reloadData]; [self.tableView reloadData];
[self.tableView layoutIfNeeded]; [self.tableView layoutIfNeeded];
[self updateFooterVisibility]; [self updateFooterVisibility];
NSLog(@"[KBChatTableView] 刷新后 contentSize: %@", NSStringFromCGSize(self.tableView.contentSize));
NSLog(@"[KBChatTableView] 刷新后 contentInset: %@", NSStringFromUIEdgeInsets(self.tableView.contentInset));
NSLog(@"[KBChatTableView] 刷新后 contentOffset: %@", NSStringFromCGPoint(self.tableView.contentOffset));
// Cell
for (NSInteger i = 0; i < self.messages.count; i++) {
CGRect cellRect = [self.tableView rectForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
KBAiChatMessage *msg = self.messages[i];
NSLog(@"[KBChatTableView] Cell[%ld] type=%ld, height=%.2f, text=%@",
(long)i, (long)msg.type, cellRect.size.height,
msg.text.length > 20 ? [msg.text substringToIndex:20] : msg.text);
}
NSLog(@"[KBChatTableView] ========== reloadWithMessages 结束 ==========");
if (keepOffset) { if (keepOffset) {
CGFloat newContentHeight = self.tableView.contentSize.height; CGFloat newContentHeight = self.tableView.contentSize.height;
CGFloat delta = newContentHeight - oldContentHeight; CGFloat delta = newContentHeight - oldContentHeight;
@@ -310,8 +353,21 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
} }
if (scrollToBottom) { if (scrollToBottom) {
// 使 NSLog(@"[KBChatTableView] 准备滚动到底部...");
[self scrollToBottomAnimated:NO]; // 使 dispatch_after reloadData
__weak typeof(self) weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) {
NSLog(@"[KBChatTableView] ⚠️ strongSelf 为空,跳过滚动");
return;
}
NSLog(@"[KBChatTableView] dispatch_after 执行 scrollToBottom");
NSLog(@"[KBChatTableView] 滚动前 contentSize: %@", NSStringFromCGSize(strongSelf.tableView.contentSize));
NSLog(@"[KBChatTableView] 滚动前 tableView.frame: %@", NSStringFromCGRect(strongSelf.tableView.frame));
[strongSelf scrollToBottomAnimated:NO];
NSLog(@"[KBChatTableView] scrollToBottom 后 contentOffset: %@", NSStringFromCGPoint(strongSelf.tableView.contentOffset));
});
} }
} }
@@ -400,9 +456,10 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
} }
- (void)updateFooterVisibility { - (void)updateFooterVisibility {
BOOL canLoadMore = (self.delegate && // mj_footer
[self.delegate respondsToSelector:@selector(chatTableViewDidTriggerLoadMore:)]); // BOOL canLoadMore = (self.delegate &&
self.tableView.mj_footer.hidden = !canLoadMore || self.messages.count == 0; // [self.delegate respondsToSelector:@selector(chatTableViewDidTriggerLoadMore:)]);
// self.tableView.mj_footer.hidden = !canLoadMore || self.messages.count == 0;
} }
#pragma mark - UITableViewDataSource #pragma mark - UITableViewDataSource

View File

@@ -177,19 +177,17 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
self.currentPage = 1; self.currentPage = 1;
self.hasMoreHistory = YES; self.hasMoreHistory = YES;
// messages //
NSArray *cachedMessages = [[KBAIChatMessageCacheManager shared] messagesForCompanionId:persona.personaId]; // NSArray *cachedMessages = [[KBAIChatMessageCacheManager shared] messagesForCompanionId:persona.personaId];
if (cachedMessages.count > 0) { // if (cachedMessages.count > 0) {
// 使 // self.messages = [cachedMessages mutableCopy];
self.messages = [cachedMessages mutableCopy]; // self.hasLoadedData = YES;
self.hasLoadedData = YES; // NSLog(@"[Cell] ✅ 从缓存加载personaId=%ld, 消息数=%ld", (long)persona.personaId, (long)cachedMessages.count);
NSLog(@"[Cell] ✅ 从缓存加载personaId=%ld, 消息数=%ld", (long)persona.personaId, (long)cachedMessages.count); // } else {
} else {
//
self.messages = [NSMutableArray array]; self.messages = [NSMutableArray array];
self.hasLoadedData = NO; self.hasLoadedData = NO;
NSLog(@"[Cell] ⚠️ 缓存未命中personaId=%ld, 需要请求数据", (long)persona.personaId); NSLog(@"[Cell] ⚠️ 缓存已禁用personaId=%ld, 需要请求数据", (long)persona.personaId);
} // }
// UI // UI
[self.backgroundImageView sd_setImageWithURL:[NSURL URLWithString:persona.coverImageUrl] [self.backgroundImageView sd_setImageWithURL:[NSURL URLWithString:persona.coverImageUrl]
@@ -205,6 +203,12 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
// //
[self ensureOpeningMessageAtTop]; [self ensureOpeningMessageAtTop];
NSLog(@"[KBPersonaChatCell] ========== setPersona 调试 ==========");
NSLog(@"[KBPersonaChatCell] personaId: %ld", (long)persona.personaId);
NSLog(@"[KBPersonaChatCell] messages.count: %ld", (long)self.messages.count);
NSLog(@"[KBPersonaChatCell] chatView.frame: %@", NSStringFromCGRect(self.chatView.frame));
NSLog(@"[KBPersonaChatCell] contentView.frame: %@", NSStringFromCGRect(self.contentView.frame));
// //
if (self.messages.count > 0) { if (self.messages.count > 0) {
// //
@@ -217,6 +221,8 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
[self.chatView clearMessages]; [self.chatView clearMessages];
} }
NSLog(@"[KBPersonaChatCell] ========== setPersona 结束 ==========");
[self.commentButton setTitle:persona.commentCount forState:UIControlStateNormal]; [self.commentButton setTitle:persona.commentCount forState:UIControlStateNormal];
[self.likeButton setTitle:persona.likeCount forState:UIControlStateNormal]; [self.likeButton setTitle:persona.likeCount forState:UIControlStateNormal];
self.likeButton.selected = persona.liked; self.likeButton.selected = persona.liked;
@@ -294,7 +300,10 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
} }
// //
if (weakSelf.currentPage == 1) { // dispatch_async currentPage
NSInteger loadedPage = weakSelf.currentPage;
if (loadedPage == 1) {
// //
weakSelf.messages = newMessages; weakSelf.messages = newMessages;
[weakSelf ensureOpeningMessageAtTop]; [weakSelf ensureOpeningMessageAtTop];
@@ -310,15 +319,26 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
// UI // UI
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
BOOL keepOffset = (weakSelf.currentPage != 1); __strong typeof(weakSelf) strongSelf = weakSelf;
BOOL scrollToBottom = (weakSelf.currentPage == 1); if (!strongSelf) {
[weakSelf.chatView reloadWithMessages:weakSelf.messages NSLog(@"[KBPersonaChatCell] ⚠️ strongSelf 为空,跳过刷新");
keepOffset:keepOffset return;
scrollToBottom:scrollToBottom]; }
[weakSelf.chatView endLoadMoreWithHasMoreData:weakSelf.hasMoreHistory];
// 使 currentPage
BOOL keepOffset = (loadedPage != 1);
BOOL scrollToBottom = (loadedPage == 1);
NSLog(@"[KBPersonaChatCell] 刷新 UI - loadedPage: %ld, keepOffset: %d, scrollToBottom: %d",
(long)loadedPage, keepOffset, scrollToBottom);
[strongSelf.chatView reloadWithMessages:strongSelf.messages
keepOffset:keepOffset
scrollToBottom:scrollToBottom];
[strongSelf.chatView endLoadMoreWithHasMoreData:strongSelf.hasMoreHistory];
// //
[[KBAIChatMessageCacheManager shared] saveMessages:weakSelf.messages [[KBAIChatMessageCacheManager shared] saveMessages:strongSelf.messages
forCompanionId:companionId]; forCompanionId:companionId];
}); });

View File

@@ -451,7 +451,7 @@
#pragma mark - Private #pragma mark - Private
- (void)updateChatViewBottomInset { - (void)updateChatViewBottomInset {
// 2 bottomInset VoiceInputBar // bottomInset VoiceInputBar
CGFloat bottomInset; CGFloat bottomInset;
if (self.currentKeyboardHeight > 0.0) { if (self.currentKeyboardHeight > 0.0) {
@@ -468,13 +468,13 @@
bottomInset = (self.currentKeyboardHeight + self.voiceInputBarHeight) - chatViewPhysicalBottomSpace; bottomInset = (self.currentKeyboardHeight + self.voiceInputBarHeight) - chatViewPhysicalBottomSpace;
// //
bottomInset = MAX(bottomInset, 20); bottomInset = MAX(bottomInset, 0);
NSLog(@"[KBAIHomeVC] 键盘弹起 - bottomInset: %.2f (键盘: %.2f + InputBar: %.2f - 已避开: %.2f)", NSLog(@"[KBAIHomeVC] 键盘弹起 - bottomInset: %.2f (键盘: %.2f + InputBar: %.2f - 已避开: %.2f)",
bottomInset, self.currentKeyboardHeight, self.voiceInputBarHeight, chatViewPhysicalBottomSpace); bottomInset, self.currentKeyboardHeight, self.voiceInputBarHeight, chatViewPhysicalBottomSpace);
} else { } else {
// bottomInset // bottomInset
bottomInset = 20; // bottomInset = 0;
NSLog(@"[KBAIHomeVC] 键盘隐藏 - bottomInset: %.2f", bottomInset); NSLog(@"[KBAIHomeVC] 键盘隐藏 - bottomInset: %.2f", bottomInset);
} }
@@ -486,21 +486,11 @@
// //
if (self.currentKeyboardHeight > 0.0) { if (self.currentKeyboardHeight > 0.0) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[cell.chatView scrollToBottom]; // 使 [cell.chatView scrollToBottom];
}); });
} }
} }
} }
NSLog(@"[KBAIHomeVC] 更新 ChatView bottomInset: %.2f (键盘高度: %.2f)", bottomInset, self.currentKeyboardHeight);
for (NSIndexPath *indexPath in self.collectionView.indexPathsForVisibleItems) {
KBPersonaChatCell *cell = (KBPersonaChatCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
if (cell) {
[cell updateChatViewBottomInset:bottomInset];
}
}
} }
- (void)showChatLimitPopWithMessage:(NSString *)message { - (void)showChatLimitPopWithMessage:(NSString *)message {