处理滚动底部问题
This commit is contained in:
@@ -27,6 +27,12 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/// 添加用户消息
|
||||
- (void)addUserMessage:(NSString *)text;
|
||||
|
||||
/// 添加加载中的用户消息
|
||||
- (void)addLoadingUserMessage;
|
||||
|
||||
/// 更新最后一条用户消息
|
||||
- (void)updateLastUserMessage:(NSString *)text;
|
||||
|
||||
/// 添加 AI 消息(带语音)
|
||||
- (void)addAssistantMessage:(NSString *)text
|
||||
audioDuration:(NSTimeInterval)duration
|
||||
@@ -42,6 +48,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/// 标记最后一条 AI 消息完成
|
||||
- (void)markLastAssistantMessageComplete;
|
||||
|
||||
/// 标记最后一条用户消息结束加载
|
||||
- (void)markLastUserMessageLoadingComplete;
|
||||
|
||||
/// 清空所有消息
|
||||
- (void)clearMessages;
|
||||
|
||||
|
||||
@@ -132,6 +132,27 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
|
||||
[self addMessage:message autoScroll:YES];
|
||||
}
|
||||
|
||||
- (void)addLoadingUserMessage {
|
||||
KBAiChatMessage *message = [KBAiChatMessage loadingUserMessage];
|
||||
[self addMessage:message autoScroll:YES];
|
||||
}
|
||||
|
||||
- (void)updateLastUserMessage:(NSString *)text {
|
||||
for (NSInteger i = self.messages.count - 1; i >= 0; i--) {
|
||||
KBAiChatMessage *message = self.messages[i];
|
||||
if (message.type == KBAiChatMessageTypeUser && message.isLoading) {
|
||||
message.text = text;
|
||||
message.isLoading = NO;
|
||||
message.isComplete = YES;
|
||||
|
||||
// 刷新该行
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
|
||||
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addAssistantMessage:(NSString *)text
|
||||
audioDuration:(NSTimeInterval)duration
|
||||
audioData:(NSData *)audioData {
|
||||
@@ -198,9 +219,12 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
|
||||
for (NSInteger i = self.messages.count - 1; i >= 0; i--) {
|
||||
KBAiChatMessage *message = self.messages[i];
|
||||
if (message.type == KBAiChatMessageTypeUser) {
|
||||
message.isLoading = NO;
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
|
||||
[self.tableView reloadRowsAtIndexPaths:@[indexPath]
|
||||
withRowAnimation:UITableViewRowAnimationNone];
|
||||
[self.tableView layoutIfNeeded];
|
||||
[self scrollToBottomAnimated:NO];
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -227,14 +251,9 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
|
||||
CGFloat tableViewHeight = self.tableView.bounds.size.height;
|
||||
CGFloat bottomInset = self.tableView.contentInset.bottom;
|
||||
|
||||
// 如果内容高度小于 tableView 高度,不需要滚动
|
||||
if (contentHeight <= tableViewHeight) {
|
||||
NSLog(@"[KBChatTableView] 内容高度(%.2f) <= tableView高度(%.2f),不需要滚动", contentHeight, tableViewHeight);
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算滚动到底部的 offset
|
||||
CGFloat offsetY = contentHeight - tableViewHeight + bottomInset;
|
||||
offsetY = MAX(0, offsetY);
|
||||
|
||||
NSLog(@"[KBChatTableView] scrollToBottom - contentHeight: %.2f, tableViewHeight: %.2f, bottomInset: %.2f, offsetY: %.2f",
|
||||
contentHeight, tableViewHeight, bottomInset, offsetY);
|
||||
@@ -312,6 +331,12 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.tableView layoutIfNeeded]; // 再次确保布局完成
|
||||
[self scrollToBottomAnimated:YES];
|
||||
|
||||
// 二次滚动以确保底部完全可见(解决自动行高导致的布局偏差)
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[self.tableView layoutIfNeeded];
|
||||
[self scrollToBottomAnimated:NO];
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
@property (nonatomic, strong) UIView *bubbleView;
|
||||
@property (nonatomic, strong) UILabel *messageLabel;
|
||||
@property (nonatomic, strong) UIActivityIndicatorView *loadingIndicator;
|
||||
|
||||
@end
|
||||
|
||||
@@ -44,12 +45,20 @@
|
||||
self.messageLabel.textColor = [UIColor blackColor];
|
||||
[self.bubbleView addSubview:self.messageLabel];
|
||||
|
||||
// 加载指示器
|
||||
self.loadingIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
|
||||
self.loadingIndicator.hidesWhenStopped = YES;
|
||||
[self.contentView addSubview:self.loadingIndicator];
|
||||
|
||||
// 布局约束
|
||||
[self.bubbleView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.equalTo(self.contentView).offset(4);
|
||||
make.bottom.equalTo(self.contentView).offset(-4);
|
||||
make.right.equalTo(self.contentView).offset(-16);
|
||||
make.width.lessThanOrEqualTo(self.contentView).multipliedBy(0.75);
|
||||
// 最小高度约束,确保 loading 时气泡不消失
|
||||
make.height.greaterThanOrEqualTo(@40);
|
||||
make.width.greaterThanOrEqualTo(@50);
|
||||
}];
|
||||
|
||||
[self.messageLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
@@ -58,10 +67,24 @@
|
||||
make.left.equalTo(self.bubbleView).offset(12);
|
||||
make.right.equalTo(self.bubbleView).offset(-12);
|
||||
}];
|
||||
|
||||
[self.loadingIndicator mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerY.equalTo(self.contentView);
|
||||
make.right.equalTo(self.contentView).offset(-16);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)configureWithMessage:(KBAiChatMessage *)message {
|
||||
self.messageLabel.text = message.text;
|
||||
|
||||
if (message.isLoading) {
|
||||
self.bubbleView.hidden = YES;
|
||||
[self.loadingIndicator startAnimating];
|
||||
} else {
|
||||
self.bubbleView.hidden = NO;
|
||||
self.messageLabel.hidden = NO;
|
||||
[self.loadingIndicator stopAnimating];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -24,6 +24,15 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/// 添加用户消息
|
||||
- (void)appendUserMessage:(NSString *)text;
|
||||
|
||||
/// 标记最后一条用户消息结束加载
|
||||
- (void)markLastUserMessageLoadingComplete;
|
||||
|
||||
/// 添加加载中的用户消息
|
||||
- (void)appendLoadingUserMessage;
|
||||
|
||||
/// 更新最后一条用户消息
|
||||
- (void)updateLastUserMessage:(NSString *)text;
|
||||
|
||||
/// 添加 AI 消息(支持打字机效果)
|
||||
- (void)appendAssistantMessage:(NSString *)text
|
||||
audioId:(nullable NSString *)audioId;
|
||||
|
||||
@@ -453,8 +453,43 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
|
||||
[self.chatView addMessage:message autoScroll:YES];
|
||||
}
|
||||
|
||||
- (void)appendLoadingUserMessage {
|
||||
if (!self.messages) {
|
||||
self.messages = [NSMutableArray array];
|
||||
}
|
||||
|
||||
[self ensureOpeningMessageAtTop];
|
||||
KBAiChatMessage *message = [KBAiChatMessage loadingUserMessage];
|
||||
[self.messages addObject:message];
|
||||
[self.chatView addMessage:message autoScroll:YES];
|
||||
}
|
||||
|
||||
- (void)updateLastUserMessage:(NSString *)text {
|
||||
[self.chatView updateLastUserMessage:text];
|
||||
|
||||
// 更新数据源中的消息
|
||||
for (NSInteger i = self.messages.count - 1; i >= 0; i--) {
|
||||
KBAiChatMessage *message = self.messages[i];
|
||||
if (message.type == KBAiChatMessageTypeUser && message.isLoading) {
|
||||
message.text = text;
|
||||
message.isLoading = NO;
|
||||
message.isComplete = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)markLastUserMessageLoadingComplete {
|
||||
// [self.chatView markLastUserMessageLoadingComplete];
|
||||
[self.chatView markLastUserMessageLoadingComplete];
|
||||
|
||||
// 同步更新数据源
|
||||
for (NSInteger i = self.messages.count - 1; i >= 0; i--) {
|
||||
KBAiChatMessage *message = self.messages[i];
|
||||
if (message.type == KBAiChatMessageTypeUser && message.isLoading) {
|
||||
message.isLoading = NO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)appendAssistantMessage:(NSString *)text
|
||||
|
||||
Reference in New Issue
Block a user