Files
keyboard/keyBoard/Class/AiTalk/VC/KBAIHomeVC.m

660 lines
22 KiB
Mathematica
Raw Normal View History

2026-01-26 16:53:41 +08:00
//
// KBAIHomeVC.m
// keyBoard
//
// Created by Mac on 2026/1/26.
//
#import "KBAIHomeVC.h"
#import "KBPersonaChatCell.h"
#import "KBPersonaModel.h"
2026-01-26 18:17:02 +08:00
#import "KBVoiceInputBar.h"
2026-01-27 13:57:32 +08:00
#import "KBVoiceRecordManager.h"
2026-01-26 18:43:07 +08:00
#import "KBVoiceToTextManager.h"
2026-01-26 16:53:41 +08:00
#import "AiVM.h"
2026-01-27 13:57:32 +08:00
#import "KBHUD.h"
2026-01-27 16:28:17 +08:00
#import "KBChatLimitPopView.h"
#import "KBVipPay.h"
#import "KBUserSessionManager.h"
#import "LSTPopView.h"
2026-01-26 16:53:41 +08:00
#import <Masonry/Masonry.h>
2026-01-27 16:28:17 +08:00
@interface KBAIHomeVC () <UICollectionViewDelegate, UICollectionViewDataSource, KBVoiceToTextManagerDelegate, KBVoiceRecordManagerDelegate, UIGestureRecognizerDelegate, KBChatLimitPopViewDelegate>
2026-01-26 16:53:41 +08:00
///
@property (nonatomic, strong) UICollectionView *collectionView;
2026-01-26 18:17:02 +08:00
///
@property (nonatomic, strong) KBVoiceInputBar *voiceInputBar;
2026-01-27 16:28:17 +08:00
@property (nonatomic, strong) MASConstraint *voiceInputBarBottomConstraint;
@property (nonatomic, assign) CGFloat voiceInputBarHeight;
@property (nonatomic, assign) CGFloat baseInputBarBottomSpacing;
@property (nonatomic, assign) CGFloat currentKeyboardHeight;
@property (nonatomic, strong) UITapGestureRecognizer *dismissKeyboardTap;
@property (nonatomic, weak) LSTPopView *chatLimitPopView;
///
@property (nonatomic, strong) UIView *bottomBackgroundView;
@property (nonatomic, strong) UIVisualEffectView *bottomBlurEffectView;
@property (nonatomic, strong) CAGradientLayer *bottomMaskLayer;
2026-01-26 18:17:02 +08:00
2026-01-26 18:43:07 +08:00
///
@property (nonatomic, strong) KBVoiceToTextManager *voiceToTextManager;
2026-01-27 13:57:32 +08:00
///
@property (nonatomic, strong) KBVoiceRecordManager *voiceRecordManager;
2026-01-26 16:53:41 +08:00
///
@property (nonatomic, strong) NSMutableArray<KBPersonaModel *> *personas;
///
@property (nonatomic, assign) NSInteger currentPage;
///
@property (nonatomic, assign) BOOL hasMore;
///
@property (nonatomic, assign) BOOL isLoading;
///
@property (nonatomic, assign) NSInteger currentIndex;
///
@property (nonatomic, strong) NSMutableSet<NSNumber *> *preloadedIndexes;
/// AiVM
@property (nonatomic, strong) AiVM *aiVM;
@end
@implementation KBAIHomeVC
#pragma mark - Lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.kb_navView.hidden = true;
//
self.personas = [NSMutableArray array];
self.currentPage = 1;
self.hasMore = YES;
self.isLoading = NO;
self.currentIndex = 0;
self.preloadedIndexes = [NSMutableSet set];
self.aiVM = [[AiVM alloc] init];
[self setupUI];
2026-01-26 18:43:07 +08:00
[self setupVoiceToTextManager];
2026-01-27 13:57:32 +08:00
[self setupVoiceRecordManager];
2026-01-27 16:28:17 +08:00
[self setupKeyboardNotifications];
[self setupKeyboardDismissGesture];
2026-01-26 16:53:41 +08:00
[self loadPersonas];
}
2026-01-27 16:28:17 +08:00
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
if (self.bottomMaskLayer) {
self.bottomMaskLayer.frame = self.bottomBlurEffectView.bounds;
}
}
2026-01-26 16:53:41 +08:00
#pragma mark - 1
- (void)setupUI {
2026-01-27 16:28:17 +08:00
self.voiceInputBarHeight = 150.0;
self.baseInputBarBottomSpacing = 20.0;
2026-01-26 16:53:41 +08:00
[self.view addSubview:self.collectionView];
[self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
2026-01-26 18:17:02 +08:00
2026-01-27 16:28:17 +08:00
//
[self.view addSubview:self.bottomBackgroundView];
[self.bottomBackgroundView addSubview:self.bottomBlurEffectView];
[self.bottomBackgroundView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.view);
// self.bottomBackgroundBottomConstraint = make.bottom.equalTo(self.view).offset(-self.baseInputBarBottomSpacing);
make.bottom.equalTo(self.view);
make.height.mas_equalTo(self.voiceInputBarHeight);
}];
[self.bottomBlurEffectView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.bottomBackgroundView);
}];
2026-01-26 18:17:02 +08:00
//
[self.view addSubview:self.voiceInputBar];
[self.voiceInputBar mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.view);
2026-01-27 16:28:17 +08:00
self.voiceInputBarBottomConstraint = make.bottom.equalTo(self.view).offset(-self.baseInputBarBottomSpacing);
make.height.mas_equalTo(self.voiceInputBarHeight); //
2026-01-26 18:17:02 +08:00
}];
2026-01-26 16:53:41 +08:00
}
#pragma mark - 2
- (void)loadPersonas {
if (self.isLoading) {
return;
}
self.isLoading = YES;
__weak typeof(self) weakSelf = self;
[self.aiVM fetchPersonasWithPageNum:self.currentPage
pageSize:10
completion:^(KBPersonaPageModel * _Nullable pageModel, NSError * _Nullable error) {
weakSelf.isLoading = NO;
if (error) {
NSLog(@"加载人设列表失败:%@", error.localizedDescription);
// TODO:
return;
}
if (!pageModel || !pageModel.records) {
NSLog(@"人设列表数据为空");
return;
}
//
[weakSelf.personas addObjectsFromArray:pageModel.records];
weakSelf.hasMore = pageModel.hasMore;
// UI
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.collectionView reloadData];
// 3
if (weakSelf.currentPage == 1) {
[weakSelf preloadDataForIndexes:@[@0, @1, @2]];
}
});
NSLog(@"加载成功:当前 %ld 条,总共 %ld 条,还有更多:%@",
weakSelf.personas.count,
pageModel.total,
pageModel.hasMore ? @"是" : @"否");
}];
}
- (void)loadMorePersonas {
if (!self.hasMore || self.isLoading) {
return;
}
self.currentPage++;
[self loadPersonas];
}
#pragma mark - 3
- (void)preloadAdjacentCellsForIndex:(NSInteger)index {
if (index < 0 || index >= self.personas.count) {
return;
}
NSMutableArray *indexesToPreload = [NSMutableArray array];
//
if (index > 0) {
[indexesToPreload addObject:@(index - 1)];
}
//
[indexesToPreload addObject:@(index)];
//
if (index < self.personas.count - 1) {
[indexesToPreload addObject:@(index + 1)];
}
[self preloadDataForIndexes:indexesToPreload];
}
- (void)preloadDataForIndexes:(NSArray<NSNumber *> *)indexes {
for (NSNumber *indexNum in indexes) {
if ([self.preloadedIndexes containsObject:indexNum]) {
continue;
}
[self.preloadedIndexes addObject:indexNum];
NSInteger index = [indexNum integerValue];
if (index >= self.personas.count) {
continue;
}
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0];
KBPersonaChatCell *cell = (KBPersonaChatCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
if (cell) {
[cell preloadDataIfNeeded];
}
NSLog(@"预加载第 %ld 个人设", (long)index);
}
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.personas.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
KBPersonaChatCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"KBPersonaChatCell" forIndexPath:indexPath];
cell.persona = self.personas[indexPath.item];
2026-01-27 16:28:17 +08:00
[self updateChatViewBottomInset];
2026-01-26 18:17:02 +08:00
//
[self.preloadedIndexes addObject:@(indexPath.item)];
//
[cell preloadDataIfNeeded];
2026-01-26 16:53:41 +08:00
return cell;
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat pageHeight = scrollView.bounds.size.height;
CGFloat offsetY = scrollView.contentOffset.y;
NSInteger currentPage = offsetY / pageHeight;
// 30%
if (fmod(offsetY, pageHeight) > pageHeight * 0.3) {
[self preloadAdjacentCellsForIndex:currentPage + 1];
} else {
[self preloadAdjacentCellsForIndex:currentPage];
}
//
if (offsetY + scrollView.bounds.size.height >= scrollView.contentSize.height - pageHeight) {
[self loadMorePersonas];
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
CGFloat pageHeight = scrollView.bounds.size.height;
NSInteger currentPage = scrollView.contentOffset.y / pageHeight;
self.currentIndex = currentPage;
if (currentPage < self.personas.count) {
NSLog(@"当前在第 %ld 个人设:%@", (long)currentPage, self.personas[currentPage].name);
}
2026-01-27 16:28:17 +08:00
[self updateChatViewBottomInset];
2026-01-26 16:53:41 +08:00
}
2026-01-26 18:43:07 +08:00
#pragma mark - 4
- (void)setupVoiceToTextManager {
self.voiceToTextManager = [[KBVoiceToTextManager alloc] initWithInputBar:self.voiceInputBar];
self.voiceToTextManager.delegate = self;
2026-01-27 13:57:32 +08:00
self.voiceToTextManager.deepgramEnabled = NO;
2026-01-26 18:43:07 +08:00
[self.voiceToTextManager prepareConnection];
}
2026-01-27 13:57:32 +08:00
/// 5
- (void)setupVoiceRecordManager {
self.voiceRecordManager = [[KBVoiceRecordManager alloc] init];
self.voiceRecordManager.delegate = self;
self.voiceRecordManager.minRecordDuration = 1.0;
}
2026-01-27 16:28:17 +08:00
#pragma mark - 6
- (void)setupKeyboardNotifications {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleKeyboardWillChangeFrame:)
name:UIKeyboardWillChangeFrameNotification
object:nil];
}
- (void)handleKeyboardWillChangeFrame:(NSNotification *)notification {
NSDictionary *userInfo = notification.userInfo;
CGRect endFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
NSTimeInterval duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
UIViewAnimationOptions options = ([userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue] << 16);
CGRect convertedFrame = [self.view convertRect:endFrame fromView:nil];
CGFloat keyboardHeight = MAX(0.0, CGRectGetMaxY(self.view.bounds) - CGRectGetMinY(convertedFrame));
self.currentKeyboardHeight = keyboardHeight;
CGFloat bottomSpacing = (keyboardHeight > 0.0) ? (keyboardHeight + 8.0) : self.baseInputBarBottomSpacing;
[self.voiceInputBarBottomConstraint setOffset:-bottomSpacing];
[self updateChatViewBottomInset];
[UIView animateWithDuration:duration
delay:0
options:options
animations:^{
[self.view layoutIfNeeded];
}
completion:nil];
}
#pragma mark - 7
- (void)setupKeyboardDismissGesture {
self.dismissKeyboardTap = [[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(handleBackgroundTap)];
self.dismissKeyboardTap.cancelsTouchesInView = NO;
self.dismissKeyboardTap.delegate = self;
[self.view addGestureRecognizer:self.dismissKeyboardTap];
}
- (void)handleBackgroundTap {
[self.view endEditing:YES];
}
#pragma mark - UIGestureRecognizerDelegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if ([touch.view isDescendantOfView:self.voiceInputBar]) {
return NO;
}
return YES;
}
2026-01-26 18:51:37 +08:00
- (NSInteger)currentCompanionId {
if (self.personas.count == 0) {
return 0;
}
NSInteger index = self.currentIndex;
if (index < 0 || index >= self.personas.count) {
NSIndexPath *indexPath = self.collectionView.indexPathsForVisibleItems.firstObject;
if (indexPath) {
index = indexPath.item;
} else {
index = 0;
}
}
KBPersonaModel *persona = self.personas[index];
return persona.personaId;
}
2026-01-26 20:36:51 +08:00
- (KBPersonaChatCell *)currentPersonaCell {
if (self.personas.count == 0) {
return nil;
}
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:self.currentIndex inSection:0];
KBPersonaChatCell *cell = (KBPersonaChatCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
if (cell) {
return cell;
}
for (NSIndexPath *visibleIndex in self.collectionView.indexPathsForVisibleItems) {
KBPersonaChatCell *visibleCell = (KBPersonaChatCell *)[self.collectionView cellForItemAtIndexPath:visibleIndex];
if (visibleCell) {
return visibleCell;
}
}
return nil;
}
2026-01-27 16:28:17 +08:00
#pragma mark - Private
- (void)updateChatViewBottomInset {
CGFloat bottomSpacing = (self.currentKeyboardHeight > 0.0) ? (self.currentKeyboardHeight + 8.0) : self.baseInputBarBottomSpacing;
CGFloat bottomInset = self.voiceInputBarHeight + bottomSpacing;
for (NSIndexPath *indexPath in self.collectionView.indexPathsForVisibleItems) {
KBPersonaChatCell *cell = (KBPersonaChatCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
if (cell) {
[cell updateChatViewBottomInset:bottomInset];
}
}
}
- (void)showChatLimitPopWithMessage:(NSString *)message {
if (self.chatLimitPopView) {
[self.chatLimitPopView dismiss];
}
CGFloat width = KB_SCREEN_WIDTH - 60;
KBChatLimitPopView *content = [[KBChatLimitPopView alloc] initWithFrame:CGRectMake(0, 0, width, 180)];
content.message = message;
content.delegate = self;
LSTPopView *pop = [LSTPopView initWithCustomView:content
parentView:nil
popStyle:LSTPopStyleFade
dismissStyle:LSTDismissStyleFade];
pop.bgColor = [[UIColor blackColor] colorWithAlphaComponent:0.4];
pop.hemStyle = LSTHemStyleCenter;
pop.isClickBgDismiss = YES;
pop.isAvoidKeyboard = NO;
self.chatLimitPopView = pop;
[pop pop];
}
2026-01-26 16:53:41 +08:00
#pragma mark - Lazy Load
- (UICollectionView *)collectionView {
if (!_collectionView) {
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.scrollDirection = UICollectionViewScrollDirectionVertical;
layout.minimumLineSpacing = 0;
layout.minimumInteritemSpacing = 0;
layout.itemSize = [UIScreen mainScreen].bounds.size;
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
_collectionView.pagingEnabled = YES;
_collectionView.showsVerticalScrollIndicator = NO;
_collectionView.backgroundColor = [UIColor whiteColor];
_collectionView.delegate = self;
_collectionView.dataSource = self;
[_collectionView registerClass:[KBPersonaChatCell class] forCellWithReuseIdentifier:@"KBPersonaChatCell"];
if (@available(iOS 11.0, *)) {
_collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
}
return _collectionView;
}
2026-01-26 18:17:02 +08:00
- (KBVoiceInputBar *)voiceInputBar {
if (!_voiceInputBar) {
_voiceInputBar = [[KBVoiceInputBar alloc] init];
_voiceInputBar.statusText = @"按住按钮开始对话";
}
return _voiceInputBar;
}
2026-01-27 16:28:17 +08:00
#pragma mark - KBChatLimitPopViewDelegate
- (void)chatLimitPopViewDidTapCancel:(KBChatLimitPopView *)view {
[self.chatLimitPopView dismiss];
}
- (void)chatLimitPopViewDidTapRecharge:(KBChatLimitPopView *)view {
[self.chatLimitPopView dismiss];
if (![KBUserSessionManager shared].isLoggedIn) {
[[KBUserSessionManager shared] goLoginVC];
return;
}
KBVipPay *vc = [[KBVipPay alloc] init];
[KB_CURRENT_NAV pushViewController:vc animated:true];
}
- (UIView *)bottomBackgroundView {
if (!_bottomBackgroundView) {
_bottomBackgroundView = [[UIView alloc] init];
_bottomBackgroundView.clipsToBounds = YES;
}
return _bottomBackgroundView;
}
- (UIVisualEffectView *)bottomBlurEffectView {
if (!_bottomBlurEffectView) {
UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
_bottomBlurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
_bottomBlurEffectView.layer.mask = self.bottomMaskLayer;
}
return _bottomBlurEffectView;
}
- (CAGradientLayer *)bottomMaskLayer {
if (!_bottomMaskLayer) {
_bottomMaskLayer = [CAGradientLayer layer];
_bottomMaskLayer.startPoint = CGPointMake(0.5, 1);
_bottomMaskLayer.endPoint = CGPointMake(0.5, 0);
_bottomMaskLayer.colors = @[
(__bridge id)[UIColor whiteColor].CGColor,
(__bridge id)[UIColor whiteColor].CGColor,
(__bridge id)[UIColor clearColor].CGColor
];
_bottomMaskLayer.locations = @[@(0.0), @(0.5), @(1.0)];
}
return _bottomMaskLayer;
}
2026-01-26 18:43:07 +08:00
#pragma mark - KBVoiceToTextManagerDelegate
2026-01-26 18:17:02 +08:00
2026-01-26 18:43:07 +08:00
- (void)voiceToTextManager:(KBVoiceToTextManager *)manager
didReceiveFinalText:(NSString *)text {
2026-01-27 16:28:17 +08:00
[self handleTranscribedText:text];
}
- (void)voiceToTextManager:(KBVoiceToTextManager *)manager
didFailWithError:(NSError *)error {
NSLog(@"[KBAIHomeVC] 语音识别失败:%@", error.localizedDescription);
}
- (void)voiceToTextManagerDidBeginRecording:(KBVoiceToTextManager *)manager {
[self.voiceRecordManager startRecording];
}
- (void)voiceToTextManagerDidEndRecording:(KBVoiceToTextManager *)manager {
[self.voiceRecordManager stopRecording];
}
- (void)voiceToTextManagerDidCancelRecording:(KBVoiceToTextManager *)manager {
[self.voiceRecordManager cancelRecording];
}
#pragma mark - KBVoiceRecordManagerDelegate
- (void)voiceRecordManager:(KBVoiceRecordManager *)manager
didFinishRecordingAtURL:(NSURL *)fileURL
duration:(NSTimeInterval)duration {
NSDictionary *attributes = [[NSFileManager defaultManager]
attributesOfItemAtPath:fileURL.path
error:nil];
unsigned long long fileSize = [attributes[NSFileSize] unsignedLongLongValue];
NSLog(@"[KBAIHomeVC] 录音完成,时长: %.2fs,大小: %llu bytes", duration, fileSize);
__weak typeof(self) weakSelf = self;
[self.aiVM transcribeAudioFileAtURL:fileURL
completion:^(KBAiSpeechTranscribeResponse * _Nullable response, NSError * _Nullable error) {
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) {
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
NSLog(@"[KBAIHomeVC] 语音转文字失败:%@", error.localizedDescription);
[KBHUD showError:KBLocalized(@"语音转文字失败,请重试")];
return;
}
NSString *transcript = response.data.transcript ?: @"";
if (transcript.length == 0) {
NSLog(@"[KBAIHomeVC] 语音转文字结果为空");
[KBHUD showError:KBLocalized(@"未识别到语音内容")];
return;
}
[strongSelf handleTranscribedText:transcript];
});
}];
}
- (void)voiceRecordManagerDidRecordTooShort:(KBVoiceRecordManager *)manager {
NSLog(@"[KBAIHomeVC] 录音过短,已忽略");
[KBHUD showError:KBLocalized(@"录音时间过短,请重新录音")];
}
- (void)voiceRecordManager:(KBVoiceRecordManager *)manager
didFailWithError:(NSError *)error {
NSLog(@"[KBAIHomeVC] 录音失败:%@", error.localizedDescription);
}
#pragma mark - Private
- (void)handleTranscribedText:(NSString *)text {
2026-01-26 18:43:07 +08:00
if (text.length == 0) {
return;
}
NSLog(@"[KBAIHomeVC] 语音识别结果:%@", text);
2026-01-26 18:17:02 +08:00
2026-01-26 18:51:37 +08:00
NSInteger companionId = [self currentCompanionId];
if (companionId <= 0) {
NSLog(@"[KBAIHomeVC] companionId 无效,取消请求");
return;
}
2026-01-26 20:36:51 +08:00
KBPersonaChatCell *currentCell = [self currentPersonaCell];
if (currentCell) {
[currentCell appendUserMessage:text];
}
__weak typeof(self) weakSelf = self;
2026-01-26 18:51:37 +08:00
[self.aiVM requestChatMessageWithContent:text
companionId:companionId
completion:^(KBAiMessageResponse * _Nullable response, NSError * _Nullable error) {
2026-01-26 20:36:51 +08:00
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) {
2026-01-26 18:51:37 +08:00
return;
}
2026-01-26 20:36:51 +08:00
dispatch_async(dispatch_get_main_queue(), ^{
2026-01-27 16:28:17 +08:00
// if (error) {
// NSLog(@"[KBAIHomeVC] 请求聊天失败:%@", error.localizedDescription);
// return;
// }
if (response.code == 50030) {
NSString *message = response.message ?: @"";
[strongSelf showChatLimitPopWithMessage:message];
2026-01-26 20:36:51 +08:00
return;
}
if (!response || !response.data) {
2026-01-27 16:28:17 +08:00
NSString *message = response.message ?: @"聊天响应为空";
NSLog(@"[KBAIHomeVC] 聊天响应为空:%@", message);
if (message.length > 0) {
[KBHUD showError:message];
}
2026-01-26 20:36:51 +08:00
return;
}
NSString *aiResponse = response.data.aiResponse ?: response.data.content ?: response.data.text ?: response.data.message ?: @"";
NSString *audioId = response.data.audioId;
if (aiResponse.length == 0) {
NSLog(@"[KBAIHomeVC] AI 回复为空");
return;
}
KBPersonaChatCell *cell = [strongSelf currentPersonaCell];
if (cell) {
[cell appendAssistantMessage:aiResponse audioId:audioId];
}
});
2026-01-26 18:51:37 +08:00
}];
2026-01-26 18:17:02 +08:00
}
2026-01-27 16:28:17 +08:00
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
2026-01-27 13:57:32 +08:00
}
2026-01-26 16:53:41 +08:00
@end