// // recordViewController.m // 双子星云手机 // // Created by xd h on 2025/3/28. // #import "recordViewController.h" #import "recordBottomView.h" #import "recordingView.h" #import #import "recordFileModel.h" #import "recordFileCell.h" #import "reNameRecordFileView.h" #import "ComontAlretViewController.h" @interface recordViewController () @property (nonatomic, strong) UITableView *tableView; @property(nonatomic,strong)NSMutableArray *audioArr;//音频数据 @property(nonatomic,strong)recordBottomView *recordBottomV;//底部视图 @property(nonatomic,strong)recordingView *recordingV;//录音中 @property(nonatomic,strong)reNameRecordFileView * reNameRecordFileV; @end @implementation recordViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. _audioArr = [NSMutableArray new]; [self drawAnyView]; [self getRecordDataFun]; } - (void)drawAnyView { [self.view setBackgroundColor:HWF5F7FAColor]; self.navBarBGView.hidden = NO; [self.navigationBar setHidden:YES]; [self.toolBar setHidden:YES]; [self.titleLabel setText:NSLocalizedString(@"mine_record_title",nil)]; //底部视图 _recordBottomV = [[recordBottomView alloc] init]; [self.view addSubview:_recordBottomV]; [_recordBottomV mas_makeConstraints:^(MASConstraintMaker *make) { make.left.mas_equalTo(0.f); make.right.mas_equalTo(0.f); make.bottom.mas_equalTo(0.f); make.height.mas_equalTo(100.f + AdaptTabHeight); }]; KWeakSelf _recordBottomV.didClickRecordFun = ^{ [weakSelf didClickRecordFun]; }; [self.view addSubview:self.tableView]; [self.tableView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.mas_equalTo(0); make.right.mas_equalTo(0); make.bottom.equalTo(self.recordBottomV.mas_top).offset(0.f); //make.bottom.mas_equalTo(0); make.top.equalTo(self.navBarBGView.mas_bottom).offset(10.f); }]; } #pragma mark - 懒加载 - (UITableView *)tableView{ if (!_tableView) { _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_W, SCREEN_H - TABBARHEIGHT) style:UITableViewStylePlain]; _tableView.delegate = self; _tableView.dataSource = self; _tableView.showsVerticalScrollIndicator = NO; _tableView.showsHorizontalScrollIndicator = NO; // _tableView.contentInset = UIEdgeInsetsMake(-H_STATE_BAR, 0, 0, 0); [_tableView setSeparatorStyle:(UITableViewCellSeparatorStyleNone)]; [_tableView setSeparatorColor:[UIColor clearColor]]; [_tableView setBackgroundColor:[UIColor clearColor]]; [_tableView setTableFooterView:[UIView new]]; [_tableView setBounces:YES]; if (@available(iOS 15.0, *)) { _tableView.sectionHeaderTopPadding = 0; } //空数据引入第三方开源处理 _tableView.emptyDataSetSource = self; _tableView.emptyDataSetDelegate = self; // 下拉追加 // MJRefreshAutoNormalFooter *footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{ // [self getMoreNetWorkData]; // }]; // // NSString *text = NSLocalizedString(@"NAS_bottom_tip",nil); // [footer setTitle:text forState:MJRefreshStateNoMoreData]; // _tableView.mj_footer = footer; } return _tableView; } #pragma mark - 列表委托 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ if(!_audioArr){ return 0; } return _audioArr.count; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 1; } - (recordFileCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ __block NSInteger row = indexPath.section; static NSString *identifier = @"recordFileCell"; recordFileCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier]; cell.selectionStyle = UITableViewCellSelectionStyleNone; if (!cell){ cell = [[recordFileCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier]; [cell setSelectionStyle:UITableViewCellSelectionStyleNone]; [cell setBackgroundColor:[UIColor clearColor]]; [cell setAccessoryType:(UITableViewCellAccessoryNone)]; } if(row < _audioArr.count){ recordFileModel* dataModel = _audioArr[row]; cell.curRecordFileModel = dataModel; KWeakSelf cell.didClickRecordButFun = ^(NSInteger index) { [weakSelf didClickCellFunBy:index withRow:row]; }; } return cell; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ NSInteger row = indexPath.section; if(row >=_audioArr.count){ return 0.0; } recordFileModel *model = _audioArr[row]; if (model.showAllType) { return 150; } return 65; } - (UIView*)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { return [UIView new]; } - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{ return 12.0; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { NSInteger row = indexPath.section; if(row < _audioArr.count){ recordFileModel *model = _audioArr[row]; if(!model.showAllType){ model.showAllType = YES; [tableView reloadData]; } } } #pragma mark 空数据 - (UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView { NSString *imageName = @"common_no_data_pic"; return [UIImage imageNamed:imageName]; } - (NSAttributedString *)titleForEmptyDataSet:(UIScrollView *)scrollView { NSString *text = NSLocalizedString(@"record_no_data_tip",nil); NSDictionary *attributes = @{NSFontAttributeName: [UIFont systemFontOfSize:16.0f], NSForegroundColorAttributeName: HW999999Color}; return [[NSAttributedString alloc] initWithString:text attributes:attributes]; } //调整图片位置 - (CGFloat)verticalOffsetForEmptyDataSet:(UIScrollView *)scrollView { return -150; } #pragma mark 读取本地数据 - (void)getRecordDataFun { NSString *documentsDirectory = kSHPath_Record; NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error = nil; NSArray *fileList = [fileManager contentsOfDirectoryAtPath:documentsDirectory error:&error]; if (error) { HLog(@"Error reading contents of directory: %@", error.localizedDescription); } NSMutableArray *dataArr = [NSMutableArray new]; //BOOL isFirstType = YES; for (NSString *fileName in fileList) { NSString *filePath = [documentsDirectory stringByAppendingPathComponent:fileName]; // 获取文件属性 NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:filePath error:&error]; if (error) { NSLog(@"获取文件属性出错: %@ - %@", fileName, error.localizedDescription); error = nil; continue; } // 判断是否是目录 BOOL isDirectory = [fileAttributes[NSFileType] isEqualToString:NSFileTypeDirectory]; // 获取文件大小 unsigned long long fileSize = [fileAttributes[NSFileSize] unsignedLongLongValue]; recordFileModel *model = [recordFileModel new]; model.fileName = fileName; model.filePath = filePath; model.fileSize = fileSize; model.isDirectory = isDirectory; model.modificationDate = fileAttributes[NSFileModificationDate] ?: [NSDate date]; NSURL *fileUrl = [NSURL URLWithString:model.filePath]; NSTimeInterval time = [self getDurationOfAudioFile:fileUrl]; model.totalTime = (NSInteger)ceil(time); // 结果为 5; model.showAllType = NO; // if (isFirstType) { // model.showAllType = YES; // isFirstType = NO; // } // else{ // model.showAllType = NO; // } // 创建文件信息字典 // NSDictionary *fileInfo = @{ // @"name": fileName, // @"path": filePath, // @"size": @(fileSize), // @"isDirectory": @(isDirectory), // @"modificationDate": fileAttributes[NSFileModificationDate] ?: [NSDate date] // }; [dataArr addObject:model]; } // 假设你的对象有一个名为 dateProperty 的 NSDate 属性 NSArray *sortedArray = [dataArr sortedArrayUsingComparator:^NSComparisonResult(recordFileModel* obj1, recordFileModel* obj2) { return [obj2.modificationDate compare:obj1.modificationDate]; }]; [_audioArr removeAllObjects]; [_audioArr addObjectsFromArray:sortedArray]; recordFileModel *firstModel = _audioArr.firstObject; firstModel.showAllType = YES; } #pragma mark 获取录音文件的时长 - (NSTimeInterval)getDurationOfAudioFile:(NSURL *)fileURL { NSError *error; AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:&error]; if (error) { HLog(@"Error: %@", error.localizedDescription); return 0; } return player.duration; } #pragma mark 点击了录音 - (void)didClickRecordFun { if(_recordingV){ return; } //检测麦克风权限 AVAudioSessionRecordPermission permissionStatus = [[AVAudioSession sharedInstance] recordPermission]; // MicrophonePermissionStatus status; // switch (permissionStatus) { // case AVAudioSessionRecordPermissionUndetermined: // status = MicrophonePermissionStatusNotDetermined; // break; // case AVAudioSessionRecordPermissionDenied: // status = MicrophonePermissionStatusDenied; // break; // case AVAudioSessionRecordPermissionGranted: // status = MicrophonePermissionStatusGranted; // break; // } if (permissionStatus == AVAudioSessionRecordPermissionDenied) { NSString *tip = NSLocalizedString(@"mine_record_microphone_tip",nil); [[iToast makeText:tip] show]; return; } else if(permissionStatus == AVAudioSessionRecordPermissionUndetermined) { KWeakSelf [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) { dispatch_async(dispatch_get_main_queue(), ^{ if (granted) { [weakSelf beginRecordFun]; } else { } }); }]; return; } [self beginRecordFun]; } #pragma mark 弹起录音功能 - (void)beginRecordFun { _recordingV = [[recordingView alloc] init]; [ksharedAppDelegate.window addSubview:_recordingV]; [_recordingV mas_makeConstraints:^(MASConstraintMaker *make) { make.left.mas_equalTo(0.f); make.right.mas_equalTo(0.f); make.bottom.mas_equalTo(0.f); make.top.mas_equalTo(0); }]; NSInteger index = _audioArr.count + 1; [_recordingV beginRecordFunWith:index]; KWeakSelf _recordingV.didClickRecordEndFun = ^{ [weakSelf deleteRecordingViewFun]; }; } #pragma mark 删除录音中界面 - (void)deleteRecordingViewFun { [_recordingV removeFromSuperview]; _recordingV = nil; [self getRecordDataFun]; [self.tableView reloadData]; } #pragma mark 点击cell里面的按钮事件 //index 1 编辑 2 删除 10 播放 11 后退 12 前进 - (void)didClickCellFunBy:(NSInteger)index withRow:(NSInteger)row { switch (index) { case 1: [self didClickEditFunWithRow:row]; break; case 2: [self deleteFileByRow:row]; default: break; } } #pragma mark 点击编辑 - (void)didClickEditFunWithRow:(NSInteger)row { recordFileModel *model = nil; if (_audioArr.count > row) { model = _audioArr[row]; } else{ return; } if (_reNameRecordFileV) { [_reNameRecordFileV removeFromSuperview]; } _reNameRecordFileV = [[reNameRecordFileView alloc] init]; [ksharedAppDelegate.window addSubview:_reNameRecordFileV]; _reNameRecordFileV.fileName = model.fileName; [_reNameRecordFileV mas_makeConstraints:^(MASConstraintMaker *make) { make.left.mas_equalTo(0.0); make.right.mas_equalTo(0.0); make.top.mas_equalTo(0.0); make.bottom.mas_equalTo(0.0); }]; KWeakSelf _reNameRecordFileV.didClickOkButtonFun = ^(NSString * _Nonnull newName) { [weakSelf checkRenameByNewName:newName withRow:row]; }; } #pragma mark 重命名逻辑 - (void)checkRenameByNewName:(NSString*)newName withRow:(NSInteger)row { [_reNameRecordFileV removeFromSuperview]; if (!newName || newName.length == 0) { NSString *tip = NSLocalizedString(@"record_reName_error_tip",nil); [[iToast makeText:tip] show]; return; } recordFileModel *model = nil; if (_audioArr.count > row) { model = _audioArr[row]; } else{ return; } BOOL isRepeatType = NO; for (int i=0; i<_audioArr.count; i++) { if (i != row) { recordFileModel *preModel = _audioArr[i]; if ([preModel.fileName isEqualToString:newName]) { isRepeatType = YES; break; } } } if (isRepeatType) { NSString *tip = NSLocalizedString(@"record_reName_error_repeat_tip",nil); [[iToast makeText:tip] show]; return; } //[_reNameRecordFileV removeFromSuperview]; // 获取文件管理器 NSFileManager *fileManager = [NSFileManager defaultManager]; // 原始文件路径 NSString *documentsPath = kSHPath_Record; NSString *oldFilePath = [documentsPath stringByAppendingPathComponent:model.fileName]; // 新文件路径 NSString *newFilePath = [documentsPath stringByAppendingPathComponent:newName]; NSError *error = nil; // 重命名/移动文件 BOOL success = [fileManager moveItemAtPath:oldFilePath toPath:newFilePath error:&error]; if (!success) { NSString *tip = NSLocalizedString(@"record_reName_error_title",nil); [[iToast makeText:tip] show]; } else { NSString *tip = NSLocalizedString(@"record_reName_ok_title",nil); [[iToast makeText:tip] show]; model.fileName = newName; [_tableView reloadData]; } } #pragma mark 文件删除 - (void)deleteFileByRow:(NSInteger)row { KWeakSelf /*弹窗提示重启*/ ComontAlretViewController *nextVC = [[ComontAlretViewController alloc] initWithTiTle:NSLocalizedString(@"delete_file_title_msg",nil) msg:NSLocalizedString(@"delete_file_tip_msg",nil) imageStr:@"" cancelTitle:NSLocalizedString(@"other_cancel",nil) okTitle:NSLocalizedString(@"delete_file_title_msg",nil) isOkBtnHighlight:YES didClickOk:^{ [weakSelf sureDeleteFileByRow:row]; } didClickCancel:^{ }]; nextVC.modalPresentationStyle = UIModalPresentationCustom; [self presentViewController:nextVC animated:YES completion:^{ nextVC.view.superview.backgroundColor = [UIColor clearColor]; }]; } #pragma mark 文件删除 - (void)sureDeleteFileByRow:(NSInteger)row { recordFileModel *model = nil; if (_audioArr.count > row) { model = _audioArr[row]; } else{ return; } NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *filePath = model.filePath; // 获取要删除的文件路径 NSError *error = nil; BOOL success = [fileManager removeItemAtPath:filePath error:&error]; if (!success) { //NSLog(@"删除文件失败: %@", error.localizedDescription); [[iToast makeText:NSLocalizedString(@"record_deleteFile_fail",nil)] show]; } else { //NSLog(@"文件删除成功"); [[iToast makeText:NSLocalizedString(@"delete_file_suc_msg",nil)] show]; [_audioArr removeObject:model]; recordFileModel *firstModel = _audioArr.firstObject; firstModel.showAllType = YES; [_tableView reloadData]; } } @end