From fe59a0cb4584dafa198484fd0b888e6c4e323036 Mon Sep 17 00:00:00 2001 From: CodeST <694468528@qq.com> Date: Mon, 2 Feb 2026 13:26:38 +0800 Subject: [PATCH] 1 --- .../Class/AiTalk/V/Chat/KBChatTableView.h | 3 + .../Class/AiTalk/V/Chat/KBChatTableView.m | 85 +++++++++++++++++++ .../Class/AiTalk/V/Chat/KBPersonaChatCell.m | 62 ++++++++++---- 3 files changed, 133 insertions(+), 17 deletions(-) diff --git a/keyBoard/Class/AiTalk/V/Chat/KBChatTableView.h b/keyBoard/Class/AiTalk/V/Chat/KBChatTableView.h index 39f5a6e..94498b9 100644 --- a/keyBoard/Class/AiTalk/V/Chat/KBChatTableView.h +++ b/keyBoard/Class/AiTalk/V/Chat/KBChatTableView.h @@ -94,6 +94,9 @@ NS_ASSUME_NONNULL_BEGIN /// 刷新指定消息(按对象指针匹配) - (void)reloadMessage:(KBAiChatMessage *)message; +/// 设置介绍视图文案(使用 tableFooterView 展示;nil/空串表示不显示) +- (void)updateIntroFooterText:(nullable NSString *)text; + @end NS_ASSUME_NONNULL_END diff --git a/keyBoard/Class/AiTalk/V/Chat/KBChatTableView.m b/keyBoard/Class/AiTalk/V/Chat/KBChatTableView.m index 87d5676..47d1b1f 100644 --- a/keyBoard/Class/AiTalk/V/Chat/KBChatTableView.m +++ b/keyBoard/Class/AiTalk/V/Chat/KBChatTableView.m @@ -31,6 +31,11 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟 @property (nonatomic, strong) AiVM *aiVM; @property (nonatomic, assign) BOOL hasMoreData; @property (nonatomic, assign) CGFloat contentBottomInset; +@property (nonatomic, copy) NSString *introFooterText; +@property (nonatomic, strong) UIView *introFooterContainer; +@property (nonatomic, strong) UILabel *introFooterLabel; +@property (nonatomic, assign) CGSize lastIntroFooterTableSize; +@property (nonatomic, assign) BOOL applyingIntroFooter; @end @@ -135,6 +140,7 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟 self.tableView.transform = inverted ? CGAffineTransformMakeScale(1, -1) : CGAffineTransformIdentity; [self updateContentBottomInset:self.contentBottomInset]; + [self updateIntroFooterText:self.introFooterText]; [self.tableView reloadData]; [self.tableView layoutIfNeeded]; } @@ -880,6 +886,85 @@ static inline CGFloat KBChatAbsTimeInterval(NSTimeInterval interval) { [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; } +- (void)updateIntroFooterText:(nullable NSString *)text { + if (self.applyingIntroFooter) { + return; + } + self.applyingIntroFooter = YES; + self.introFooterText = text ?: @""; + if (self.introFooterText.length == 0) { + self.tableView.tableFooterView = nil; + self.lastIntroFooterTableSize = CGSizeZero; + self.applyingIntroFooter = NO; + return; + } + + if (!self.introFooterContainer) { + self.introFooterContainer = [[UIView alloc] initWithFrame:CGRectZero]; + self.introFooterContainer.backgroundColor = [UIColor clearColor]; + + self.introFooterLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + self.introFooterLabel.numberOfLines = 0; + self.introFooterLabel.font = [UIFont systemFontOfSize:14]; + self.introFooterLabel.textColor = [[UIColor whiteColor] colorWithAlphaComponent:0.9]; + self.introFooterLabel.textAlignment = NSTextAlignmentCenter; + [self.introFooterContainer addSubview:self.introFooterLabel]; + } + + self.introFooterLabel.text = self.introFooterText; + self.introFooterContainer.transform = self.inverted ? CGAffineTransformMakeScale(1, -1) : CGAffineTransformIdentity; + + CGFloat width = CGRectGetWidth(self.tableView.bounds); + if (width <= 0) { + width = CGRectGetWidth(self.bounds); + } + CGFloat height = CGRectGetHeight(self.tableView.bounds); + if (height <= 0) { + height = CGRectGetHeight(self.bounds); + } + self.lastIntroFooterTableSize = CGSizeMake(width, height); + + CGFloat horizontalPadding = 24; + CGFloat verticalPadding = 16; + CGFloat labelWidth = MAX(0, width - horizontalPadding * 2); + CGSize labelSize = [self.introFooterLabel sizeThatFits:CGSizeMake(labelWidth, CGFLOAT_MAX)]; + CGFloat containerHeight = MAX(height, labelSize.height + verticalPadding * 2); + + self.introFooterContainer.frame = CGRectMake(0, 0, width, containerHeight); + self.introFooterLabel.frame = CGRectMake(horizontalPadding, verticalPadding, labelWidth, labelSize.height); + self.tableView.tableFooterView = self.introFooterContainer; + + dispatch_async(dispatch_get_main_queue(), ^{ + [self.tableView layoutIfNeeded]; + CGFloat contentHeight = self.tableView.contentSize.height; + CGFloat tableHeight = CGRectGetHeight(self.tableView.bounds); + CGFloat minOffset = -self.tableView.contentInset.top; + CGFloat maxOffset = contentHeight - tableHeight + self.tableView.contentInset.bottom; + if (maxOffset < minOffset) { + maxOffset = minOffset; + } + CGFloat targetOffset = self.inverted ? maxOffset : minOffset; + [self.tableView setContentOffset:CGPointMake(0, targetOffset) animated:NO]; + }); + self.applyingIntroFooter = NO; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + if (self.introFooterText.length == 0 || self.applyingIntroFooter) { + return; + } + CGFloat width = CGRectGetWidth(self.tableView.bounds); + CGFloat height = CGRectGetHeight(self.tableView.bounds); + if (width <= 0 || height <= 0) { + return; + } + CGSize size = CGSizeMake(width, height); + if (!CGSizeEqualToSize(size, self.lastIntroFooterTableSize)) { + [self updateIntroFooterText:self.introFooterText]; + } +} + /// 判断是否需要插入时间戳 - (BOOL)shouldInsertTimestampForMessage:(KBAiChatMessage *)message { // 第一条消息总是显示时间 diff --git a/keyBoard/Class/AiTalk/V/Chat/KBPersonaChatCell.m b/keyBoard/Class/AiTalk/V/Chat/KBPersonaChatCell.m index 32fd9f9..444081d 100644 --- a/keyBoard/Class/AiTalk/V/Chat/KBPersonaChatCell.m +++ b/keyBoard/Class/AiTalk/V/Chat/KBPersonaChatCell.m @@ -175,6 +175,13 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe #pragma mark - Setter +- (NSString *)currentPrologueText { + if (self.persona.prologue.length > 0) { + return self.persona.prologue; + } + return self.persona.introText ?: @""; +} + - (void)setPersona:(KBPersonaModel *)persona { _persona = persona; @@ -203,13 +210,10 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe [self.avatarImageView sd_setImageWithURL:[NSURL URLWithString:persona.avatarUrl] placeholderImage:[UIImage imageNamed:@"placeholder_avatar"]]; self.nameLabel.text = persona.name; - self.openingLabel.text = persona.shortDesc.length > 0 ? persona.shortDesc : persona.introText; + self.openingLabel.text = persona.shortDesc.length > 0 ? persona.shortDesc : persona.prologue; // 关键修复:清空消息时停止音频播放,避免状态混乱 [self.chatView stopPlayingAudio]; - - // 确保开场白在第一条 - [self ensureOpeningMessageAtTop]; NSLog(@"[KBPersonaChatCell] ========== setPersona 调试 =========="); NSLog(@"[KBPersonaChatCell] personaId: %ld", (long)persona.personaId); @@ -217,8 +221,9 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe NSLog(@"[KBPersonaChatCell] chatView.frame: %@", NSStringFromCGRect(self.chatView.frame)); NSLog(@"[KBPersonaChatCell] contentView.frame: %@", NSStringFromCGRect(self.contentView.frame)); - // 如果有消息,直接显示(包含开场白) if (self.messages.count > 0) { + [self.chatView updateIntroFooterText:nil]; + [self ensureOpeningMessageAtTop]; // 同步缓存,避免下次从缓存缺少开场白 [[KBAIChatMessageCacheManager shared] saveMessages:self.messages forCompanionId:persona.personaId]; @@ -227,6 +232,7 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe scrollToBottom:YES]; } else { [self.chatView clearMessages]; + [self.chatView updateIntroFooterText:persona.prologue]; } NSLog(@"[KBPersonaChatCell] ========== setPersona 结束 =========="); @@ -276,7 +282,7 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe dispatch_async(dispatch_get_main_queue(), ^{ strongSelf.isLoading = NO; [strongSelf.chatView endLoadMoreWithHasMoreData:strongSelf.hasMoreHistory]; - if (strongSelf.currentPage == 1 && strongSelf.persona.introText.length > 0) { + if (strongSelf.currentPage == 1 && strongSelf.persona.prologue.length > 0) { [strongSelf showOpeningMessage]; } }); @@ -286,6 +292,18 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe strongSelf.hasLoadedData = YES; strongSelf.hasMoreHistory = pageModel.hasMore; + NSInteger loadedPage = strongSelf.currentPage; + if (loadedPage == 1 && pageModel.total == 0) { + dispatch_async(dispatch_get_main_queue(), ^{ + [strongSelf.chatView clearMessages]; + [strongSelf.chatView updateIntroFooterText:strongSelf.persona.prologue]; + [strongSelf.chatView endLoadMoreWithHasMoreData:strongSelf.hasMoreHistory]; + strongSelf.isLoading = NO; + }); + strongSelf.currentPage++; + return; + } + // 转换为 KBAiChatMessage NSMutableArray *newMessages = [NSMutableArray array]; for (KBChatHistoryModel *item in pageModel.records) { @@ -313,8 +331,7 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe } // 插入历史消息(确保开场白始终是第一条) - // 关键修复:在 dispatch_async 之前保存当前页码,避免异步执行时 currentPage 已经被递增 - NSInteger loadedPage = strongSelf.currentPage; + [strongSelf.chatView updateIntroFooterText:nil]; if (loadedPage == 1) { // 第一页,直接赋值 @@ -384,7 +401,13 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe } - (void)showOpeningMessage { - // 显示开场白作为第一条消息 + if (self.messages.count == 0) { + [self.chatView clearMessages]; + [self.chatView updateIntroFooterText:self.persona.prologue]; + return; + } + + [self.chatView updateIntroFooterText:nil]; [self ensureOpeningMessageAtTop]; dispatch_async(dispatch_get_main_queue(), ^{ @@ -408,16 +431,16 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe if (!message) { return NO; } - NSString *introText = self.persona.introText ?: @""; - if (introText.length == 0) { + NSString *prologue = [self currentPrologueText]; + if (prologue.length == 0) { return NO; } - return (message.type == KBAiChatMessageTypeAssistant) && [message.text isEqualToString:introText]; + return (message.type == KBAiChatMessageTypeAssistant) && [message.text isEqualToString:prologue]; } - (void)ensureOpeningMessageAtTop { - NSString *introText = self.persona.introText ?: @""; - if (introText.length == 0) { + NSString *prologue = [self currentPrologueText]; + if (prologue.length == 0) { return; } if (!self.messages) { @@ -426,7 +449,7 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe if ([self hasOpeningMessageAtTop]) { return; } - KBAiChatMessage *openingMsg = [KBAiChatMessage assistantMessageWithText:introText]; + KBAiChatMessage *openingMsg = [KBAiChatMessage assistantMessageWithText:prologue]; openingMsg.isComplete = YES; openingMsg.needsTypewriterEffect = NO; if (self.chatView.inverted) { @@ -445,8 +468,8 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe } - (NSInteger)openingMessageIndexInMessages { - NSString *introText = self.persona.introText ?: @""; - if (introText.length == 0 || self.messages.count == 0) { + NSString *prologue = [self currentPrologueText]; + if (prologue.length == 0 || self.messages.count == 0) { return NSNotFound; } @@ -500,6 +523,7 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe self.messages = [NSMutableArray array]; } + [self.chatView updateIntroFooterText:nil]; [self ensureOpeningMessageAtTop]; KBAiChatMessage *message = [KBAiChatMessage userMessageWithText:text]; if (self.chatView.inverted) { @@ -519,6 +543,7 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe self.messages = [NSMutableArray array]; } + [self.chatView updateIntroFooterText:nil]; [self ensureOpeningMessageAtTop]; KBAiChatMessage *message = [KBAiChatMessage loadingUserMessage]; if (self.chatView.inverted) { @@ -589,6 +614,7 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe self.messages = [NSMutableArray array]; } + [self.chatView updateIntroFooterText:nil]; [self ensureOpeningMessageAtTop]; // 查找并移除 loading 消息 @@ -611,6 +637,7 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe self.messages = [NSMutableArray array]; } + [self.chatView updateIntroFooterText:nil]; [self ensureOpeningMessageAtTop]; KBAiChatMessage *message = [KBAiChatMessage loadingAssistantMessage]; if (self.chatView.inverted) { @@ -635,6 +662,7 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe self.messages = [NSMutableArray array]; } + [self.chatView updateIntroFooterText:nil]; [self ensureOpeningMessageAtTop]; KBAiChatMessage *message = [KBAiChatMessage loadingAssistantMessage]; self.pendingAssistantMessages[requestId] = message;