From 43922966169c2f25321eea9c188937b18456e486 Mon Sep 17 00:00:00 2001 From: CodeST <694468528@qq.com> Date: Thu, 29 Jan 2026 13:13:42 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=81=8A=E5=A4=A9=E6=9C=80?= =?UTF-8?q?=E5=90=8E=E4=B8=80=E6=9D=A1=E8=B7=91=E5=88=B0=E6=9C=80=E4=B8=8A?= =?UTF-8?q?=E9=9D=A2=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- keyBoard/Class/AiTalk/V/KBChatTableView.m | 107 +++++++++++++++----- keyBoard/Class/AiTalk/V/KBPersonaChatCell.m | 58 +++++++---- keyBoard/Class/AiTalk/VC/KBAIHomeVC.m | 20 +--- 3 files changed, 126 insertions(+), 59 deletions(-) diff --git a/keyBoard/Class/AiTalk/V/KBChatTableView.m b/keyBoard/Class/AiTalk/V/KBChatTableView.m index 1d6671e..cd5f33d 100644 --- a/keyBoard/Class/AiTalk/V/KBChatTableView.m +++ b/keyBoard/Class/AiTalk/V/KBChatTableView.m @@ -90,10 +90,13 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟 make.edges.equalTo(self); }]; - // 关键修复:减少初始 contentBottomInset,因为 chatView 已经通过约束避开了底部 - self.contentBottomInset = 20; // 简单的缓冲空间 + // 初始化 contentInset + self.contentBottomInset = 0; [self updateContentBottomInset:self.contentBottomInset]; - + + // 暂时禁用 mj_footer,排查问题 + // TODO: 如果需要加载更多功能,重新启用 + /* __weak typeof(self) weakSelf = self; self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{ __strong typeof(weakSelf) strongSelf = weakSelf; @@ -114,11 +117,12 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟 }]; // 隐藏"已经全部加载完毕"的提示文字 - MJRefreshAutoNormalFooter *footer = (MJRefreshAutoNormalFooter *)self.tableView.mj_footer; - footer.stateLabel.hidden = YES; // 隐藏状态文字 - footer.refreshingBlock = footer.refreshingBlock; // 保持刷新逻辑 + // MJRefreshAutoNormalFooter *footer = (MJRefreshAutoNormalFooter *)self.tableView.mj_footer; + // footer.stateLabel.hidden = YES; // 隐藏状态文字 + // footer.refreshingBlock = footer.refreshingBlock; // 保持刷新逻辑 - self.tableView.mj_footer.hidden = YES; + // self.tableView.mj_footer.hidden = YES; + */ } #pragma mark - Public Methods @@ -206,39 +210,56 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟 // 关键修复:使用 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; - // 直接滚动到最后一条消息,不检查是否可见(确保新消息能被看到) - [self.tableView scrollToRowAtIndexPath:lastIndexPath - atScrollPosition:UITableViewScrollPositionBottom - animated:animated]; + // 如果内容高度小于 tableView 高度,不需要滚动 + if (contentHeight <= tableViewHeight) { + NSLog(@"[KBChatTableView] 内容高度(%.2f) <= tableView高度(%.2f),不需要滚动", contentHeight, tableViewHeight); + 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 - (void)endLoadMoreWithHasMoreData:(BOOL)hasMoreData { self.hasMoreData = hasMoreData; - if (hasMoreData) { - [self.tableView.mj_footer endRefreshing]; - } else { - [self.tableView.mj_footer endRefreshingWithNoMoreData]; - } + // 暂时禁用 mj_footer + // if (hasMoreData) { + // [self.tableView.mj_footer endRefreshing]; + // } else { + // [self.tableView.mj_footer endRefreshingWithNoMoreData]; + // } [self updateFooterVisibility]; } - (void)resetNoMoreData { self.hasMoreData = YES; - [self.tableView.mj_footer resetNoMoreData]; + // 暂时禁用 mj_footer + // [self.tableView.mj_footer resetNoMoreData]; [self updateFooterVisibility]; } - (void)updateContentBottomInset:(CGFloat)bottomInset { self.contentBottomInset = bottomInset; - UIEdgeInsets insets = self.tableView.contentInset; + + // 直接设置 contentInset + UIEdgeInsets insets = UIEdgeInsetsZero; insets.bottom = bottomInset; self.tableView.contentInset = insets; self.tableView.scrollIndicatorInsets = insets; + + NSLog(@"[KBChatTableView] updateContentBottomInset: %.2f", bottomInset); } - (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 layoutIfNeeded]; [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) { CGFloat newContentHeight = self.tableView.contentSize.height; CGFloat delta = newContentHeight - oldContentHeight; @@ -310,8 +353,21 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟 } if (scrollToBottom) { - // 关键修复:直接滚动,不使用延迟 - [self scrollToBottomAnimated:NO]; + NSLog(@"[KBChatTableView] 准备滚动到底部..."); + // 关键修复:使用 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 { - BOOL canLoadMore = (self.delegate && - [self.delegate respondsToSelector:@selector(chatTableViewDidTriggerLoadMore:)]); - self.tableView.mj_footer.hidden = !canLoadMore || self.messages.count == 0; + // 暂时禁用 mj_footer + // BOOL canLoadMore = (self.delegate && + // [self.delegate respondsToSelector:@selector(chatTableViewDidTriggerLoadMore:)]); + // self.tableView.mj_footer.hidden = !canLoadMore || self.messages.count == 0; } #pragma mark - UITableViewDataSource diff --git a/keyBoard/Class/AiTalk/V/KBPersonaChatCell.m b/keyBoard/Class/AiTalk/V/KBPersonaChatCell.m index 0e59354..c059e75 100644 --- a/keyBoard/Class/AiTalk/V/KBPersonaChatCell.m +++ b/keyBoard/Class/AiTalk/V/KBPersonaChatCell.m @@ -177,19 +177,17 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe self.currentPage = 1; self.hasMoreHistory = YES; - // ✅ 尝试从缓存加载 messages - NSArray *cachedMessages = [[KBAIChatMessageCacheManager shared] messagesForCompanionId:persona.personaId]; - if (cachedMessages.count > 0) { - // 缓存命中,直接使用 - self.messages = [cachedMessages mutableCopy]; - self.hasLoadedData = YES; - NSLog(@"[Cell] ✅ 从缓存加载:personaId=%ld, 消息数=%ld", (long)persona.personaId, (long)cachedMessages.count); - } else { - // 缓存未命中,需要请求 + // ⚠️ 临时禁用缓存,排查问题 + // NSArray *cachedMessages = [[KBAIChatMessageCacheManager shared] messagesForCompanionId:persona.personaId]; + // if (cachedMessages.count > 0) { + // self.messages = [cachedMessages mutableCopy]; + // self.hasLoadedData = YES; + // NSLog(@"[Cell] ✅ 从缓存加载:personaId=%ld, 消息数=%ld", (long)persona.personaId, (long)cachedMessages.count); + // } else { self.messages = [NSMutableArray array]; self.hasLoadedData = NO; - NSLog(@"[Cell] ⚠️ 缓存未命中:personaId=%ld, 需要请求数据", (long)persona.personaId); - } + NSLog(@"[Cell] ⚠️ 缓存已禁用:personaId=%ld, 需要请求数据", (long)persona.personaId); + // } // 设置 UI [self.backgroundImageView sd_setImageWithURL:[NSURL URLWithString:persona.coverImageUrl] @@ -205,6 +203,12 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe // 确保开场白在第一条 [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) { // 同步缓存,避免下次从缓存缺少开场白 @@ -217,6 +221,8 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe [self.chatView clearMessages]; } + NSLog(@"[KBPersonaChatCell] ========== setPersona 结束 =========="); + [self.commentButton setTitle:persona.commentCount forState:UIControlStateNormal]; [self.likeButton setTitle:persona.likeCount forState:UIControlStateNormal]; 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 ensureOpeningMessageAtTop]; @@ -310,15 +319,26 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe // 刷新 UI dispatch_async(dispatch_get_main_queue(), ^{ - BOOL keepOffset = (weakSelf.currentPage != 1); - BOOL scrollToBottom = (weakSelf.currentPage == 1); - [weakSelf.chatView reloadWithMessages:weakSelf.messages - keepOffset:keepOffset - scrollToBottom:scrollToBottom]; - [weakSelf.chatView endLoadMoreWithHasMoreData:weakSelf.hasMoreHistory]; + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { + NSLog(@"[KBPersonaChatCell] ⚠️ strongSelf 为空,跳过刷新"); + return; + } + + // 使用保存的页码判断,而不是 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]; }); diff --git a/keyBoard/Class/AiTalk/VC/KBAIHomeVC.m b/keyBoard/Class/AiTalk/VC/KBAIHomeVC.m index cf37381..9a0ed25 100644 --- a/keyBoard/Class/AiTalk/VC/KBAIHomeVC.m +++ b/keyBoard/Class/AiTalk/VC/KBAIHomeVC.m @@ -451,7 +451,7 @@ #pragma mark - Private - (void)updateChatViewBottomInset { - // 问题2修复:键盘弹起时,增加 bottomInset 让最后一条消息显示在 VoiceInputBar 上方 + // 键盘弹起时,增加 bottomInset 让最后一条消息显示在 VoiceInputBar 上方 CGFloat bottomInset; if (self.currentKeyboardHeight > 0.0) { @@ -468,13 +468,13 @@ bottomInset = (self.currentKeyboardHeight + self.voiceInputBarHeight) - chatViewPhysicalBottomSpace; // 确保不会是负数 - bottomInset = MAX(bottomInset, 20); + bottomInset = MAX(bottomInset, 0); NSLog(@"[KBAIHomeVC] 键盘弹起 - bottomInset: %.2f (键盘: %.2f + InputBar: %.2f - 已避开: %.2f)", bottomInset, self.currentKeyboardHeight, self.voiceInputBarHeight, chatViewPhysicalBottomSpace); } else { - // 键盘隐藏时:恢复原来的 bottomInset - bottomInset = 20; // 简单的缓冲空间 + // 键盘隐藏时:不需要额外的 bottomInset + bottomInset = 0; NSLog(@"[KBAIHomeVC] 键盘隐藏 - bottomInset: %.2f", bottomInset); } @@ -486,21 +486,11 @@ // 键盘弹起时,自动滚动到最后一条消息 if (self.currentKeyboardHeight > 0.0) { 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 {