发送文本处理ui和逻辑

This commit is contained in:
2026-01-29 17:56:53 +08:00
parent b556e6841d
commit d0c5cada35
3 changed files with 193 additions and 81 deletions

View File

@@ -46,6 +46,9 @@ typedef NS_ENUM(NSInteger, KBVoiceInputBarState) {
/// 代理 /// 代理
@property (nonatomic, weak) id<KBVoiceInputBarDelegate> delegate; @property (nonatomic, weak) id<KBVoiceInputBarDelegate> delegate;
/// 文本发送回调(文本模式下发送消息时触发)
@property (nonatomic, copy) void (^onTextSend)(NSString *text);
/// 状态文本(显示在按钮上方) /// 状态文本(显示在按钮上方)
@property (nonatomic, copy) NSString *statusText; @property (nonatomic, copy) NSString *statusText;

View File

@@ -408,8 +408,10 @@
} }
- (void)handleTextCenterTap { - (void)handleTextCenterTap {
self.inputState = KBVoiceInputBarStateText; // VC
[self.hiddenTextField becomeFirstResponder]; if (self.onTextSend) {
self.onTextSend(nil);
}
} }
- (void)handleVoiceLongPress:(UILongPressGestureRecognizer *)gesture { - (void)handleVoiceLongPress:(UILongPressGestureRecognizer *)gesture {

View File

@@ -20,7 +20,7 @@
#import "KBAIMessageVC.h" #import "KBAIMessageVC.h"
#import <Masonry/Masonry.h> #import <Masonry/Masonry.h>
@interface KBAIHomeVC () <UICollectionViewDelegate, UICollectionViewDataSource, KBVoiceToTextManagerDelegate, KBVoiceRecordManagerDelegate, UIGestureRecognizerDelegate, KBChatLimitPopViewDelegate> @interface KBAIHomeVC () <UICollectionViewDelegate, UICollectionViewDataSource, KBVoiceToTextManagerDelegate, KBVoiceRecordManagerDelegate, UIGestureRecognizerDelegate, KBChatLimitPopViewDelegate, UITextViewDelegate>
/// ///
@property (nonatomic, strong) UICollectionView *collectionView; @property (nonatomic, strong) UICollectionView *collectionView;
@@ -31,11 +31,24 @@
@property (nonatomic, assign) CGFloat voiceInputBarHeight; @property (nonatomic, assign) CGFloat voiceInputBarHeight;
@property (nonatomic, assign) CGFloat baseInputBarBottomSpacing; @property (nonatomic, assign) CGFloat baseInputBarBottomSpacing;
@property (nonatomic, assign) CGFloat currentKeyboardHeight; @property (nonatomic, assign) CGFloat currentKeyboardHeight;
/// KBVoiceInputBar /// "由 KBVoiceInputBar 触发的键盘"
@property (nonatomic, assign) BOOL voiceInputKeyboardActive; @property (nonatomic, assign) BOOL voiceInputKeyboardActive;
@property (nonatomic, strong) UITapGestureRecognizer *dismissKeyboardTap; @property (nonatomic, strong) UITapGestureRecognizer *dismissKeyboardTap;
@property (nonatomic, weak) LSTPopView *chatLimitPopView; @property (nonatomic, weak) LSTPopView *chatLimitPopView;
///
@property (nonatomic, strong) UIView *textInputContainerView;
///
@property (nonatomic, strong) UITextView *textInputTextView;
///
@property (nonatomic, strong) UIButton *sendButton;
///
@property (nonatomic, strong) UILabel *placeholderLabel;
///
@property (nonatomic, strong) MASConstraint *textInputContainerBottomConstraint;
///
@property (nonatomic, assign) BOOL isTextInputMode;
/// ///
@property (nonatomic, strong) UIView *bottomBackgroundView; @property (nonatomic, strong) UIView *bottomBackgroundView;
@property (nonatomic, strong) UIVisualEffectView *bottomBlurEffectView; @property (nonatomic, strong) UIVisualEffectView *bottomBlurEffectView;
@@ -94,12 +107,16 @@
return nil; return nil;
} }
/// KBVoiceInputBar /// KBVoiceInputBar
- (BOOL)kb_isKeyboardFromVoiceInputBar { - (BOOL)kb_isKeyboardFromVoiceInputBar {
UIView *firstResponder = [self kb_findFirstResponderInView:self.view]; UIView *firstResponder = [self kb_findFirstResponderInView:self.view];
if (!firstResponder) { if (!firstResponder) {
return NO; return NO;
} }
// textInputTextView
if (firstResponder == self.textInputTextView) {
return YES;
}
return [firstResponder isDescendantOfView:self.voiceInputBar]; return [firstResponder isDescendantOfView:self.voiceInputBar];
} }
@@ -117,9 +134,12 @@
self.currentIndex = 0; self.currentIndex = 0;
self.preloadedIndexes = [NSMutableSet set]; self.preloadedIndexes = [NSMutableSet set];
self.aiVM = [[AiVM alloc] init]; self.aiVM = [[AiVM alloc] init];
self.isWaitingForAIResponse = NO; // self.isWaitingForAIResponse = NO;
self.isTextInputMode = NO;
[self setupUI]; [self setupUI];
[self setupTextInputView];
[self setupVoiceInputBarCallback];
[self setupVoiceToTextManager]; [self setupVoiceToTextManager];
[self setupVoiceRecordManager]; [self setupVoiceRecordManager];
[self setupKeyboardNotifications]; [self setupKeyboardNotifications];
@@ -151,13 +171,12 @@
make.right.equalTo(self.view).offset(-16); make.right.equalTo(self.view).offset(-16);
make.width.height.mas_equalTo(32); make.width.height.mas_equalTo(32);
}]; }];
// //
[self.view addSubview:self.bottomBackgroundView]; [self.view addSubview:self.bottomBackgroundView];
[self.bottomBackgroundView addSubview:self.bottomBlurEffectView]; [self.bottomBackgroundView addSubview:self.bottomBlurEffectView];
[self.bottomBackgroundView mas_makeConstraints:^(MASConstraintMaker *make) { [self.bottomBackgroundView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.view); make.left.right.equalTo(self.view);
// self.bottomBackgroundBottomConstraint = make.bottom.equalTo(self.view).offset(-self.baseInputBarBottomSpacing);
make.bottom.equalTo(self.view); make.bottom.equalTo(self.view);
make.height.mas_equalTo(self.voiceInputBarHeight); make.height.mas_equalTo(self.voiceInputBarHeight);
}]; }];
@@ -170,10 +189,73 @@
[self.voiceInputBar mas_makeConstraints:^(MASConstraintMaker *make) { [self.voiceInputBar mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.view); make.left.right.equalTo(self.view);
self.voiceInputBarBottomConstraint = make.bottom.equalTo(self.view).offset(-self.baseInputBarBottomSpacing); self.voiceInputBarBottomConstraint = make.bottom.equalTo(self.view).offset(-self.baseInputBarBottomSpacing);
make.height.mas_equalTo(self.voiceInputBarHeight); // make.height.mas_equalTo(self.voiceInputBarHeight);
}]; }];
} }
///
- (void)setupTextInputView {
//
[self.view addSubview:self.textInputContainerView];
[self.textInputContainerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.view);
self.textInputContainerBottomConstraint = make.bottom.equalTo(self.view).offset(100); //
make.height.mas_greaterThanOrEqualTo(50);
}];
[self.textInputContainerView addSubview:self.textInputTextView];
[self.textInputContainerView addSubview:self.sendButton];
[self.textInputTextView addSubview:self.placeholderLabel];
[self.sendButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.textInputContainerView).offset(-16);
make.bottom.equalTo(self.textInputContainerView).offset(-10);
make.width.mas_equalTo(60);
make.height.mas_equalTo(36);
}];
[self.textInputTextView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.textInputContainerView).offset(16);
make.right.equalTo(self.sendButton.mas_left).offset(-10);
make.top.equalTo(self.textInputContainerView).offset(8);
make.bottom.equalTo(self.textInputContainerView).offset(-8);
make.height.mas_greaterThanOrEqualTo(36);
make.height.mas_lessThanOrEqualTo(100);
}];
[self.placeholderLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.textInputTextView).offset(5);
make.top.equalTo(self.textInputTextView).offset(8);
}];
}
/// VoiceInputBar
- (void)setupVoiceInputBarCallback {
__weak typeof(self) weakSelf = self;
self.voiceInputBar.onTextSend = ^(NSString *text) {
//
[weakSelf showTextInputView];
};
}
///
- (void)showTextInputView {
self.isTextInputMode = YES;
self.voiceInputBar.hidden = YES;
self.textInputContainerView.hidden = NO;
[self.textInputTextView becomeFirstResponder];
}
///
- (void)hideTextInputView {
self.isTextInputMode = NO;
[self.textInputTextView resignFirstResponder];
self.textInputContainerView.hidden = YES;
self.voiceInputBar.hidden = NO;
self.textInputTextView.text = @"";
self.placeholderLabel.hidden = NO;
}
#pragma mark - 2 #pragma mark - 2
- (void)loadPersonas { - (void)loadPersonas {
@@ -191,7 +273,6 @@
if (error) { if (error) {
NSLog(@"加载人设列表失败:%@", error.localizedDescription); NSLog(@"加载人设列表失败:%@", error.localizedDescription);
// TODO:
return; return;
} }
@@ -200,24 +281,18 @@
return; return;
} }
//
[weakSelf.personas addObjectsFromArray:pageModel.records]; [weakSelf.personas addObjectsFromArray:pageModel.records];
weakSelf.hasMore = pageModel.hasMore; weakSelf.hasMore = pageModel.hasMore;
// UI
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.collectionView reloadData]; [weakSelf.collectionView reloadData];
// 3
if (weakSelf.currentPage == 1) { if (weakSelf.currentPage == 1) {
[weakSelf preloadDataForIndexes:@[@0, @1, @2]]; [weakSelf preloadDataForIndexes:@[@0, @1, @2]];
} }
}); });
NSLog(@"加载成功:当前 %ld 条,总共 %ld 条,还有更多:%@", NSLog(@"加载成功:当前 %ld 条,总共 %ld 条,还有更多:%@",
weakSelf.personas.count, weakSelf.personas.count, pageModel.total, pageModel.hasMore ? @"是" : @"否");
pageModel.total,
pageModel.hasMore ? @"是" : @"否");
}]; }];
} }
@@ -225,7 +300,6 @@
if (!self.hasMore || self.isLoading) { if (!self.hasMore || self.isLoading) {
return; return;
} }
self.currentPage++; self.currentPage++;
[self loadPersonas]; [self loadPersonas];
} }
@@ -238,20 +312,13 @@
} }
NSMutableArray *indexesToPreload = [NSMutableArray array]; NSMutableArray *indexesToPreload = [NSMutableArray array];
//
if (index > 0) { if (index > 0) {
[indexesToPreload addObject:@(index - 1)]; [indexesToPreload addObject:@(index - 1)];
} }
//
[indexesToPreload addObject:@(index)]; [indexesToPreload addObject:@(index)];
//
if (index < self.personas.count - 1) { if (index < self.personas.count - 1) {
[indexesToPreload addObject:@(index + 1)]; [indexesToPreload addObject:@(index + 1)];
} }
[self preloadDataForIndexes:indexesToPreload]; [self preloadDataForIndexes:indexesToPreload];
} }
@@ -260,7 +327,6 @@
if ([self.preloadedIndexes containsObject:indexNum]) { if ([self.preloadedIndexes containsObject:indexNum]) {
continue; continue;
} }
[self.preloadedIndexes addObject:indexNum]; [self.preloadedIndexes addObject:indexNum];
NSInteger index = [indexNum integerValue]; NSInteger index = [indexNum integerValue];
@@ -270,11 +336,9 @@
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0]; NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0];
KBPersonaChatCell *cell = (KBPersonaChatCell *)[self.collectionView cellForItemAtIndexPath:indexPath]; KBPersonaChatCell *cell = (KBPersonaChatCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
if (cell) { if (cell) {
[cell preloadDataIfNeeded]; [cell preloadDataIfNeeded];
} }
NSLog(@"预加载第 %ld 个人设", (long)index); NSLog(@"预加载第 %ld 个人设", (long)index);
} }
} }
@@ -289,20 +353,14 @@
KBPersonaChatCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"KBPersonaChatCell" forIndexPath:indexPath]; KBPersonaChatCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"KBPersonaChatCell" forIndexPath:indexPath];
cell.persona = self.personas[indexPath.item]; cell.persona = self.personas[indexPath.item];
[self updateChatViewBottomInset]; [self updateChatViewBottomInset];
//
[self.preloadedIndexes addObject:@(indexPath.item)]; [self.preloadedIndexes addObject:@(indexPath.item)];
//
[cell preloadDataIfNeeded]; [cell preloadDataIfNeeded];
return cell; return cell;
} }
#pragma mark - UIScrollViewDelegate #pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView { - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// AI
if (self.isWaitingForAIResponse) { if (self.isWaitingForAIResponse) {
return; return;
} }
@@ -311,14 +369,12 @@
CGFloat offsetY = scrollView.contentOffset.y; CGFloat offsetY = scrollView.contentOffset.y;
NSInteger currentPage = offsetY / pageHeight; NSInteger currentPage = offsetY / pageHeight;
// 30%
if (fmod(offsetY, pageHeight) > pageHeight * 0.3) { if (fmod(offsetY, pageHeight) > pageHeight * 0.3) {
[self preloadAdjacentCellsForIndex:currentPage + 1]; [self preloadAdjacentCellsForIndex:currentPage + 1];
} else { } else {
[self preloadAdjacentCellsForIndex:currentPage]; [self preloadAdjacentCellsForIndex:currentPage];
} }
//
if (offsetY + scrollView.bounds.size.height >= scrollView.contentSize.height - pageHeight) { if (offsetY + scrollView.bounds.size.height >= scrollView.contentSize.height - pageHeight) {
[self loadMorePersonas]; [self loadMorePersonas];
} }
@@ -332,15 +388,12 @@
if (currentPage < self.personas.count) { if (currentPage < self.personas.count) {
NSLog(@"当前在第 %ld 个人设:%@", (long)currentPage, self.personas[currentPage].name); NSLog(@"当前在第 %ld 个人设:%@", (long)currentPage, self.personas[currentPage].name);
} }
[self updateChatViewBottomInset]; [self updateChatViewBottomInset];
} }
/// AI
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
if (self.isWaitingForAIResponse) { if (self.isWaitingForAIResponse) {
NSLog(@"[KBAIHomeVC] 正在等待 AI 回复,禁止滚动"); NSLog(@"[KBAIHomeVC] 正在等待 AI 回复,禁止滚动");
//
scrollView.scrollEnabled = NO; scrollView.scrollEnabled = NO;
scrollView.scrollEnabled = YES; scrollView.scrollEnabled = YES;
} }
@@ -377,45 +430,42 @@
NSTimeInterval duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; NSTimeInterval duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
UIViewAnimationOptions options = ([userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue] << 16); UIViewAnimationOptions options = ([userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue] << 16);
// frame view
CGRect convertedFrame = [self.view convertRect:endFrame fromView:nil]; CGRect convertedFrame = [self.view convertRect:endFrame fromView:nil];
CGFloat keyboardHeight = MAX(0.0, CGRectGetMaxY(self.view.bounds) - CGRectGetMinY(convertedFrame)); CGFloat keyboardHeight = MAX(0.0, CGRectGetMaxY(self.view.bounds) - CGRectGetMinY(convertedFrame));
// Gate KBVoiceInputBar
if (keyboardHeight > 0.0) { if (keyboardHeight > 0.0) {
if (![self kb_isKeyboardFromVoiceInputBar]) { if (![self kb_isKeyboardFromVoiceInputBar]) {
return; return;
} }
self.voiceInputKeyboardActive = YES; self.voiceInputKeyboardActive = YES;
} else { } else {
// KBVoiceInputBar
if (!self.voiceInputKeyboardActive) { if (!self.voiceInputKeyboardActive) {
return; return;
} }
self.voiceInputKeyboardActive = NO; self.voiceInputKeyboardActive = NO;
// VoiceInputBar
if (self.isTextInputMode) {
[self hideTextInputView];
}
} }
self.currentKeyboardHeight = keyboardHeight; self.currentKeyboardHeight = keyboardHeight;
NSLog(@"[KBAIHomeVC] 键盘高度: %.2f, 屏幕高度: %.2f, 键盘 Y: %.2f", NSLog(@"[KBAIHomeVC] 键盘高度: %.2f", keyboardHeight);
keyboardHeight, CGRectGetMaxY(self.view.bounds), CGRectGetMinY(convertedFrame));
// 1 VoiceInputBar view.bottom
CGFloat bottomSpacing; CGFloat bottomSpacing;
if (keyboardHeight > 0.0) { if (keyboardHeight > 0.0) {
// VoiceInputBar 5px
// bottomSpacing = - 5px offset
bottomSpacing = keyboardHeight - 5.0; bottomSpacing = keyboardHeight - 5.0;
//
if (self.isTextInputMode) {
[self.textInputContainerBottomConstraint setOffset:-keyboardHeight];
}
} else { } else {
//
bottomSpacing = self.baseInputBarBottomSpacing; bottomSpacing = self.baseInputBarBottomSpacing;
[self.textInputContainerBottomConstraint setOffset:100]; //
} }
NSLog(@"[KBAIHomeVC] VoiceInputBar bottomSpacing: %.2f", bottomSpacing);
[self.voiceInputBarBottomConstraint setOffset:-bottomSpacing]; [self.voiceInputBarBottomConstraint setOffset:-bottomSpacing];
// 2 ChatView bottomInset
[self updateChatViewBottomInset]; [self updateChatViewBottomInset];
[UIView animateWithDuration:duration [UIView animateWithDuration:duration
@@ -447,6 +497,9 @@
if ([touch.view isDescendantOfView:self.voiceInputBar]) { if ([touch.view isDescendantOfView:self.voiceInputBar]) {
return NO; return NO;
} }
if ([touch.view isDescendantOfView:self.textInputContainerView]) {
return NO;
}
return YES; return YES;
} }
@@ -486,46 +539,27 @@
return visibleCell; return visibleCell;
} }
} }
return nil; return nil;
} }
#pragma mark - Private #pragma mark - Private
- (void)updateChatViewBottomInset { - (void)updateChatViewBottomInset {
// bottomInset VoiceInputBar
CGFloat bottomInset; CGFloat bottomInset;
if (self.currentKeyboardHeight > 0.0) { if (self.currentKeyboardHeight > 0.0) {
// CGFloat avatarBottomSpace = KB_TABBAR_HEIGHT + 50 + 20;
// avatarImageView = KB_TABBAR_HEIGHT + 50 + 20 CGFloat chatViewPhysicalBottomSpace = avatarBottomSpace + 54 + 10;
// chatView = KB_TABBAR_HEIGHT + 50 + 20 + 54() + 10() 153
// VoiceInputBar chatView
// bottomInset = + VoiceInputBar - chatView
CGFloat avatarBottomSpace = KB_TABBAR_HEIGHT + 50 + 20; // avatarImageView
CGFloat chatViewPhysicalBottomSpace = avatarBottomSpace + 54 + 10; // chatView
// = ( + InputBar) - chatView
bottomInset = (self.currentKeyboardHeight + self.voiceInputBarHeight) - chatViewPhysicalBottomSpace; bottomInset = (self.currentKeyboardHeight + self.voiceInputBarHeight) - chatViewPhysicalBottomSpace;
//
bottomInset = MAX(bottomInset, 0); bottomInset = MAX(bottomInset, 0);
NSLog(@"[KBAIHomeVC] 键盘弹起 - bottomInset: %.2f (键盘: %.2f + InputBar: %.2f - 已避开: %.2f)",
bottomInset, self.currentKeyboardHeight, self.voiceInputBarHeight, chatViewPhysicalBottomSpace);
} else { } else {
// bottomInset
bottomInset = 0; bottomInset = 0;
NSLog(@"[KBAIHomeVC] 键盘隐藏 - bottomInset: %.2f", bottomInset);
} }
for (NSIndexPath *indexPath in self.collectionView.indexPathsForVisibleItems) { for (NSIndexPath *indexPath in self.collectionView.indexPathsForVisibleItems) {
KBPersonaChatCell *cell = (KBPersonaChatCell *)[self.collectionView cellForItemAtIndexPath:indexPath]; KBPersonaChatCell *cell = (KBPersonaChatCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
if (cell) { if (cell) {
[cell updateChatViewBottomInset:bottomInset]; [cell updateChatViewBottomInset:bottomInset];
//
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];
@@ -557,6 +591,18 @@
[pop pop]; [pop pop];
} }
#pragma mark - UITextViewDelegate
- (void)textViewDidChange:(UITextView *)textView {
self.placeholderLabel.hidden = textView.text.length > 0;
//
CGSize size = [textView sizeThatFits:CGSizeMake(textView.frame.size.width, CGFLOAT_MAX)];
CGFloat newHeight = MIN(MAX(size.height, 36), 100);
[textView mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_greaterThanOrEqualTo(newHeight);
}];
}
#pragma mark - Lazy Load #pragma mark - Lazy Load
- (UICollectionView *)collectionView { - (UICollectionView *)collectionView {
@@ -590,6 +636,55 @@
return _voiceInputBar; return _voiceInputBar;
} }
- (UIView *)textInputContainerView {
if (!_textInputContainerView) {
_textInputContainerView = [[UIView alloc] init];
_textInputContainerView.backgroundColor = [UIColor whiteColor];
_textInputContainerView.hidden = YES;
}
return _textInputContainerView;
}
- (UITextView *)textInputTextView {
if (!_textInputTextView) {
_textInputTextView = [[UITextView alloc] init];
_textInputTextView.font = [UIFont systemFontOfSize:16];
_textInputTextView.textColor = [UIColor blackColor];
_textInputTextView.backgroundColor = [UIColor colorWithRed:0.95 green:0.95 blue:0.95 alpha:1.0];
_textInputTextView.layer.cornerRadius = 18;
_textInputTextView.layer.masksToBounds = YES;
_textInputTextView.textContainerInset = UIEdgeInsetsMake(8, 8, 8, 8);
_textInputTextView.delegate = self;
_textInputTextView.returnKeyType = UIReturnKeySend;
_textInputTextView.enablesReturnKeyAutomatically = YES;
}
return _textInputTextView;
}
- (UIButton *)sendButton {
if (!_sendButton) {
_sendButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_sendButton setTitle:KBLocalized(@"发送") forState:UIControlStateNormal];
[_sendButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
_sendButton.titleLabel.font = [UIFont systemFontOfSize:15 weight:UIFontWeightMedium];
_sendButton.backgroundColor = [UIColor colorWithRed:0.2 green:0.6 blue:1.0 alpha:1.0];
_sendButton.layer.cornerRadius = 18;
_sendButton.layer.masksToBounds = YES;
[_sendButton addTarget:self action:@selector(sendButtonTapped) forControlEvents:UIControlEventTouchUpInside];
}
return _sendButton;
}
- (UILabel *)placeholderLabel {
if (!_placeholderLabel) {
_placeholderLabel = [[UILabel alloc] init];
_placeholderLabel.text = KBLocalized(@"输入消息...");
_placeholderLabel.font = [UIFont systemFontOfSize:16];
_placeholderLabel.textColor = [UIColor lightGrayColor];
}
return _placeholderLabel;
}
#pragma mark - KBChatLimitPopViewDelegate #pragma mark - KBChatLimitPopViewDelegate
- (void)chatLimitPopViewDidTapCancel:(KBChatLimitPopView *)view { - (void)chatLimitPopViewDidTapCancel:(KBChatLimitPopView *)view {
@@ -654,6 +749,24 @@
[self.navigationController pushViewController:vc animated:YES]; [self.navigationController pushViewController:vc animated:YES];
} }
/// - handleTranscribedText
- (void)sendButtonTapped {
NSString *text = [self.textInputTextView.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if (text.length == 0) {
return;
}
//
self.textInputTextView.text = @"";
self.placeholderLabel.hidden = NO;
//
[self hideTextInputView];
// handleTranscribedText
[self handleTranscribedText:text];
}
#pragma mark - KBVoiceToTextManagerDelegate #pragma mark - KBVoiceToTextManagerDelegate
- (void)voiceToTextManager:(KBVoiceToTextManager *)manager - (void)voiceToTextManager:(KBVoiceToTextManager *)manager
@@ -689,7 +802,6 @@
unsigned long long fileSize = [attributes[NSFileSize] unsignedLongLongValue]; unsigned long long fileSize = [attributes[NSFileSize] unsignedLongLongValue];
NSLog(@"[KBAIHomeVC] 录音完成,时长: %.2fs,大小: %llu bytes", duration, fileSize); NSLog(@"[KBAIHomeVC] 录音完成,时长: %.2fs,大小: %llu bytes", duration, fileSize);
// loading
KBPersonaChatCell *currentCell = [self currentPersonaCell]; KBPersonaChatCell *currentCell = [self currentPersonaCell];
if (currentCell) { if (currentCell) {
[currentCell appendLoadingUserMessage]; [currentCell appendLoadingUserMessage];
@@ -725,12 +837,10 @@
return; return;
} }
// loading
if (cell) { if (cell) {
[cell updateLastUserMessage:transcript]; [cell updateLastUserMessage:transcript];
} }
//
[strongSelf handleTranscribedText:transcript appendToUI:NO]; [strongSelf handleTranscribedText:transcript appendToUI:NO];
}); });
}]; }];
@@ -756,7 +866,7 @@
if (text.length == 0) { if (text.length == 0) {
return; return;
} }
NSLog(@"[KBAIHomeVC] 语音识别结果%@", text); NSLog(@"[KBAIHomeVC] 发送消息%@", text);
NSInteger companionId = [self currentCompanionId]; NSInteger companionId = [self currentCompanionId];
if (companionId <= 0) { if (companionId <= 0) {
@@ -769,7 +879,6 @@
[currentCell appendUserMessage:text]; [currentCell appendUserMessage:text];
} }
// CollectionView
self.isWaitingForAIResponse = YES; self.isWaitingForAIResponse = YES;
self.collectionView.scrollEnabled = NO; self.collectionView.scrollEnabled = NO;
NSLog(@"[KBAIHomeVC] 开始等待 AI 回复,禁止 CollectionView 滚动"); NSLog(@"[KBAIHomeVC] 开始等待 AI 回复,禁止 CollectionView 滚动");
@@ -784,12 +893,10 @@
} }
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
// CollectionView
strongSelf.isWaitingForAIResponse = NO; strongSelf.isWaitingForAIResponse = NO;
strongSelf.collectionView.scrollEnabled = YES; strongSelf.collectionView.scrollEnabled = YES;
NSLog(@"[KBAIHomeVC] AI 回复完成,恢复 CollectionView 滚动"); NSLog(@"[KBAIHomeVC] AI 回复完成,恢复 CollectionView 滚动");
// loading
KBPersonaChatCell *cell = [strongSelf currentPersonaCell]; KBPersonaChatCell *cell = [strongSelf currentPersonaCell];
if (cell) { if (cell) {
[cell markLastUserMessageLoadingComplete]; [cell markLastUserMessageLoadingComplete];