recordViewController.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. //
  2. // recordViewController.m
  3. // 双子星云手机
  4. //
  5. // Created by xd h on 2025/3/28.
  6. //
  7. #import "recordViewController.h"
  8. #import "recordBottomView.h"
  9. #import "recordingView.h"
  10. #import <AVFoundation/AVFoundation.h>
  11. #import "recordFileModel.h"
  12. #import "recordFileCell.h"
  13. #import "reNameRecordFileView.h"
  14. #import "ComontAlretViewController.h"
  15. @interface recordViewController ()<UITableViewDelegate,UITableViewDataSource,DZNEmptyDataSetSource, DZNEmptyDataSetDelegate>
  16. @property (nonatomic, strong) UITableView *tableView;
  17. @property(nonatomic,strong)NSMutableArray *audioArr;//音频数据
  18. @property(nonatomic,strong)recordBottomView *recordBottomV;//底部视图
  19. @property(nonatomic,strong)recordingView *recordingV;//录音中
  20. @property(nonatomic,strong)reNameRecordFileView * reNameRecordFileV;
  21. @end
  22. @implementation recordViewController
  23. - (void)viewDidLoad {
  24. [super viewDidLoad];
  25. // Do any additional setup after loading the view.
  26. _audioArr = [NSMutableArray new];
  27. [self drawAnyView];
  28. [self getRecordDataFun];
  29. }
  30. - (void)drawAnyView
  31. {
  32. [self.view setBackgroundColor:HWF5F7FAColor];
  33. self.navBarBGView.hidden = NO;
  34. [self.navigationBar setHidden:YES];
  35. [self.toolBar setHidden:YES];
  36. [self.titleLabel setText:NSLocalizedString(@"mine_record_title",nil)];
  37. //底部视图
  38. _recordBottomV = [[recordBottomView alloc] init];
  39. [self.view addSubview:_recordBottomV];
  40. [_recordBottomV mas_makeConstraints:^(MASConstraintMaker *make) {
  41. make.left.mas_equalTo(0.f);
  42. make.right.mas_equalTo(0.f);
  43. make.bottom.mas_equalTo(0.f);
  44. make.height.mas_equalTo(100.f + AdaptTabHeight);
  45. }];
  46. KWeakSelf
  47. _recordBottomV.didClickRecordFun = ^{
  48. [weakSelf didClickRecordFun];
  49. };
  50. [self.view addSubview:self.tableView];
  51. [self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
  52. make.left.mas_equalTo(0);
  53. make.right.mas_equalTo(0);
  54. make.bottom.equalTo(self.recordBottomV.mas_top).offset(0.f);
  55. //make.bottom.mas_equalTo(0);
  56. make.top.equalTo(self.navBarBGView.mas_bottom).offset(10.f);
  57. }];
  58. }
  59. #pragma mark - 懒加载
  60. - (UITableView *)tableView{
  61. if (!_tableView) {
  62. _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_W, SCREEN_H - TABBARHEIGHT) style:UITableViewStylePlain];
  63. _tableView.delegate = self;
  64. _tableView.dataSource = self;
  65. _tableView.showsVerticalScrollIndicator = NO;
  66. _tableView.showsHorizontalScrollIndicator = NO;
  67. // _tableView.contentInset = UIEdgeInsetsMake(-H_STATE_BAR, 0, 0, 0);
  68. [_tableView setSeparatorStyle:(UITableViewCellSeparatorStyleNone)];
  69. [_tableView setSeparatorColor:[UIColor clearColor]];
  70. [_tableView setBackgroundColor:[UIColor clearColor]];
  71. [_tableView setTableFooterView:[UIView new]];
  72. [_tableView setBounces:YES];
  73. if (@available(iOS 15.0, *)) {
  74. _tableView.sectionHeaderTopPadding = 0;
  75. }
  76. //空数据引入第三方开源处理
  77. _tableView.emptyDataSetSource = self;
  78. _tableView.emptyDataSetDelegate = self;
  79. // 下拉追加
  80. // MJRefreshAutoNormalFooter *footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
  81. // [self getMoreNetWorkData];
  82. // }];
  83. //
  84. // NSString *text = NSLocalizedString(@"NAS_bottom_tip",nil);
  85. // [footer setTitle:text forState:MJRefreshStateNoMoreData];
  86. // _tableView.mj_footer = footer;
  87. }
  88. return _tableView;
  89. }
  90. #pragma mark - 列表委托
  91. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
  92. if(!_audioArr){
  93. return 0;
  94. }
  95. return _audioArr.count;
  96. }
  97. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  98. return 1;
  99. }
  100. - (recordFileCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
  101. __block NSInteger row = indexPath.section;
  102. static NSString *identifier = @"recordFileCell";
  103. recordFileCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier];
  104. cell.selectionStyle = UITableViewCellSelectionStyleNone;
  105. if (!cell){
  106. cell = [[recordFileCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier];
  107. [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
  108. [cell setBackgroundColor:[UIColor clearColor]];
  109. [cell setAccessoryType:(UITableViewCellAccessoryNone)];
  110. }
  111. if(row < _audioArr.count){
  112. recordFileModel* dataModel = _audioArr[row];
  113. cell.curRecordFileModel = dataModel;
  114. KWeakSelf
  115. cell.didClickRecordButFun = ^(NSInteger index) {
  116. [weakSelf didClickCellFunBy:index withRow:row];
  117. };
  118. }
  119. return cell;
  120. }
  121. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
  122. NSInteger row = indexPath.section;
  123. if(row >=_audioArr.count){
  124. return 0.0;
  125. }
  126. recordFileModel *model = _audioArr[row];
  127. if (model.showAllType) {
  128. return 150;
  129. }
  130. return 65;
  131. }
  132. - (UIView*)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
  133. {
  134. return [UIView new];
  135. }
  136. - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
  137. return 12.0;
  138. }
  139. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  140. {
  141. NSInteger row = indexPath.section;
  142. if(row < _audioArr.count){
  143. recordFileModel *model = _audioArr[row];
  144. if(!model.showAllType){
  145. model.showAllType = YES;
  146. [tableView reloadData];
  147. }
  148. }
  149. }
  150. #pragma mark 空数据
  151. - (UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView {
  152. NSString *imageName = @"common_no_data_pic";
  153. return [UIImage imageNamed:imageName];
  154. }
  155. - (NSAttributedString *)titleForEmptyDataSet:(UIScrollView *)scrollView {
  156. NSString *text = NSLocalizedString(@"record_no_data_tip",nil);
  157. NSDictionary *attributes = @{NSFontAttributeName: [UIFont systemFontOfSize:16.0f],
  158. NSForegroundColorAttributeName: HW999999Color};
  159. return [[NSAttributedString alloc] initWithString:text attributes:attributes];
  160. }
  161. //调整图片位置
  162. - (CGFloat)verticalOffsetForEmptyDataSet:(UIScrollView *)scrollView {
  163. return -150;
  164. }
  165. #pragma mark 读取本地数据
  166. - (void)getRecordDataFun
  167. {
  168. NSString *documentsDirectory = kSHPath_Record;
  169. NSFileManager *fileManager = [NSFileManager defaultManager];
  170. NSError *error = nil;
  171. NSArray *fileList = [fileManager contentsOfDirectoryAtPath:documentsDirectory error:&error];
  172. if (error) {
  173. HLog(@"Error reading contents of directory: %@", error.localizedDescription);
  174. }
  175. NSMutableArray *dataArr = [NSMutableArray new];
  176. //BOOL isFirstType = YES;
  177. for (NSString *fileName in fileList) {
  178. NSString *filePath = [documentsDirectory stringByAppendingPathComponent:fileName];
  179. // 获取文件属性
  180. NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:filePath error:&error];
  181. if (error) {
  182. NSLog(@"获取文件属性出错: %@ - %@", fileName, error.localizedDescription);
  183. error = nil;
  184. continue;
  185. }
  186. // 判断是否是目录
  187. BOOL isDirectory = [fileAttributes[NSFileType] isEqualToString:NSFileTypeDirectory];
  188. // 获取文件大小
  189. unsigned long long fileSize = [fileAttributes[NSFileSize] unsignedLongLongValue];
  190. recordFileModel *model = [recordFileModel new];
  191. model.fileName = fileName;
  192. model.filePath = filePath;
  193. model.fileSize = fileSize;
  194. model.isDirectory = isDirectory;
  195. model.modificationDate = fileAttributes[NSFileModificationDate] ?: [NSDate date];
  196. NSURL *fileUrl = [NSURL URLWithString:model.filePath];
  197. NSTimeInterval time = [self getDurationOfAudioFile:fileUrl];
  198. model.totalTime = (NSInteger)ceil(time); // 结果为 5;
  199. model.showAllType = NO;
  200. // if (isFirstType) {
  201. // model.showAllType = YES;
  202. // isFirstType = NO;
  203. // }
  204. // else{
  205. // model.showAllType = NO;
  206. // }
  207. // 创建文件信息字典
  208. // NSDictionary *fileInfo = @{
  209. // @"name": fileName,
  210. // @"path": filePath,
  211. // @"size": @(fileSize),
  212. // @"isDirectory": @(isDirectory),
  213. // @"modificationDate": fileAttributes[NSFileModificationDate] ?: [NSDate date]
  214. // };
  215. [dataArr addObject:model];
  216. }
  217. // 假设你的对象有一个名为 dateProperty 的 NSDate 属性
  218. NSArray *sortedArray = [dataArr sortedArrayUsingComparator:^NSComparisonResult(recordFileModel* obj1, recordFileModel* obj2) {
  219. return [obj2.modificationDate compare:obj1.modificationDate];
  220. }];
  221. [_audioArr removeAllObjects];
  222. [_audioArr addObjectsFromArray:sortedArray];
  223. recordFileModel *firstModel = _audioArr.firstObject;
  224. firstModel.showAllType = YES;
  225. }
  226. #pragma mark 获取录音文件的时长
  227. - (NSTimeInterval)getDurationOfAudioFile:(NSURL *)fileURL {
  228. NSError *error;
  229. AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:&error];
  230. if (error) {
  231. HLog(@"Error: %@", error.localizedDescription);
  232. return 0;
  233. }
  234. return player.duration;
  235. }
  236. #pragma mark 点击了录音
  237. - (void)didClickRecordFun
  238. {
  239. if(_recordingV){
  240. return;
  241. }
  242. //检测麦克风权限
  243. AVAudioSessionRecordPermission permissionStatus = [[AVAudioSession sharedInstance] recordPermission];
  244. // MicrophonePermissionStatus status;
  245. // switch (permissionStatus) {
  246. // case AVAudioSessionRecordPermissionUndetermined:
  247. // status = MicrophonePermissionStatusNotDetermined;
  248. // break;
  249. // case AVAudioSessionRecordPermissionDenied:
  250. // status = MicrophonePermissionStatusDenied;
  251. // break;
  252. // case AVAudioSessionRecordPermissionGranted:
  253. // status = MicrophonePermissionStatusGranted;
  254. // break;
  255. // }
  256. if (permissionStatus == AVAudioSessionRecordPermissionDenied) {
  257. NSString *tip = NSLocalizedString(@"mine_record_microphone_tip",nil);
  258. [[iToast makeText:tip] show];
  259. return;
  260. }
  261. else if(permissionStatus == AVAudioSessionRecordPermissionUndetermined) {
  262. KWeakSelf
  263. [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
  264. dispatch_async(dispatch_get_main_queue(), ^{
  265. if (granted) {
  266. [weakSelf beginRecordFun];
  267. } else {
  268. }
  269. });
  270. }];
  271. return;
  272. }
  273. [self beginRecordFun];
  274. }
  275. #pragma mark 弹起录音功能
  276. - (void)beginRecordFun
  277. {
  278. _recordingV = [[recordingView alloc] init];
  279. [ksharedAppDelegate.window addSubview:_recordingV];
  280. [_recordingV mas_makeConstraints:^(MASConstraintMaker *make) {
  281. make.left.mas_equalTo(0.f);
  282. make.right.mas_equalTo(0.f);
  283. make.bottom.mas_equalTo(0.f);
  284. make.top.mas_equalTo(0);
  285. }];
  286. NSInteger index = _audioArr.count + 1;
  287. [_recordingV beginRecordFunWith:index];
  288. KWeakSelf
  289. _recordingV.didClickRecordEndFun = ^{
  290. [weakSelf deleteRecordingViewFun];
  291. };
  292. }
  293. #pragma mark 删除录音中界面
  294. - (void)deleteRecordingViewFun
  295. {
  296. [_recordingV removeFromSuperview];
  297. _recordingV = nil;
  298. [self getRecordDataFun];
  299. [self.tableView reloadData];
  300. }
  301. #pragma mark 点击cell里面的按钮事件 //index 1 编辑 2 删除 10 播放 11 后退 12 前进
  302. - (void)didClickCellFunBy:(NSInteger)index withRow:(NSInteger)row
  303. {
  304. switch (index) {
  305. case 1:
  306. [self didClickEditFunWithRow:row];
  307. break;
  308. case 2:
  309. [self deleteFileByRow:row];
  310. default:
  311. break;
  312. }
  313. }
  314. #pragma mark 点击编辑
  315. - (void)didClickEditFunWithRow:(NSInteger)row
  316. {
  317. recordFileModel *model = nil;
  318. if (_audioArr.count > row) {
  319. model = _audioArr[row];
  320. }
  321. else{
  322. return;
  323. }
  324. if (_reNameRecordFileV) {
  325. [_reNameRecordFileV removeFromSuperview];
  326. }
  327. _reNameRecordFileV = [[reNameRecordFileView alloc] init];
  328. [ksharedAppDelegate.window addSubview:_reNameRecordFileV];
  329. _reNameRecordFileV.fileName = model.fileName;
  330. [_reNameRecordFileV mas_makeConstraints:^(MASConstraintMaker *make) {
  331. make.left.mas_equalTo(0.0);
  332. make.right.mas_equalTo(0.0);
  333. make.top.mas_equalTo(0.0);
  334. make.bottom.mas_equalTo(0.0);
  335. }];
  336. KWeakSelf
  337. _reNameRecordFileV.didClickOkButtonFun = ^(NSString * _Nonnull newName) {
  338. [weakSelf checkRenameByNewName:newName withRow:row];
  339. };
  340. }
  341. #pragma mark 重命名逻辑
  342. - (void)checkRenameByNewName:(NSString*)newName withRow:(NSInteger)row
  343. {
  344. [_reNameRecordFileV removeFromSuperview];
  345. if (!newName || newName.length == 0) {
  346. NSString *tip = NSLocalizedString(@"record_reName_error_tip",nil);
  347. [[iToast makeText:tip] show];
  348. return;
  349. }
  350. recordFileModel *model = nil;
  351. if (_audioArr.count > row) {
  352. model = _audioArr[row];
  353. }
  354. else{
  355. return;
  356. }
  357. BOOL isRepeatType = NO;
  358. for (int i=0; i<_audioArr.count; i++) {
  359. if (i != row) {
  360. recordFileModel *preModel = _audioArr[i];
  361. if ([preModel.fileName isEqualToString:newName]) {
  362. isRepeatType = YES;
  363. break;
  364. }
  365. }
  366. }
  367. if (isRepeatType) {
  368. NSString *tip = NSLocalizedString(@"record_reName_error_repeat_tip",nil);
  369. [[iToast makeText:tip] show];
  370. return;
  371. }
  372. //[_reNameRecordFileV removeFromSuperview];
  373. // 获取文件管理器
  374. NSFileManager *fileManager = [NSFileManager defaultManager];
  375. // 原始文件路径
  376. NSString *documentsPath = kSHPath_Record;
  377. NSString *oldFilePath = [documentsPath stringByAppendingPathComponent:model.fileName];
  378. // 新文件路径
  379. NSString *newFilePath = [documentsPath stringByAppendingPathComponent:newName];
  380. NSError *error = nil;
  381. // 重命名/移动文件
  382. BOOL success = [fileManager moveItemAtPath:oldFilePath toPath:newFilePath error:&error];
  383. if (!success) {
  384. NSString *tip = NSLocalizedString(@"record_reName_error_title",nil);
  385. [[iToast makeText:tip] show];
  386. } else {
  387. NSString *tip = NSLocalizedString(@"record_reName_ok_title",nil);
  388. [[iToast makeText:tip] show];
  389. model.fileName = newName;
  390. [_tableView reloadData];
  391. }
  392. }
  393. #pragma mark 文件删除
  394. - (void)deleteFileByRow:(NSInteger)row
  395. {
  396. KWeakSelf
  397. /*弹窗提示重启*/
  398. ComontAlretViewController *nextVC = [[ComontAlretViewController alloc] initWithTiTle:NSLocalizedString(@"delete_file_title_msg",nil)
  399. msg:NSLocalizedString(@"delete_file_tip_msg",nil)
  400. imageStr:@""
  401. cancelTitle:NSLocalizedString(@"other_cancel",nil)
  402. okTitle:NSLocalizedString(@"delete_file_title_msg",nil) isOkBtnHighlight:YES
  403. didClickOk:^{
  404. [weakSelf sureDeleteFileByRow:row];
  405. } didClickCancel:^{
  406. }];
  407. nextVC.modalPresentationStyle = UIModalPresentationCustom;
  408. [self presentViewController:nextVC animated:YES completion:^{
  409. nextVC.view.superview.backgroundColor = [UIColor clearColor];
  410. }];
  411. }
  412. #pragma mark 文件删除
  413. - (void)sureDeleteFileByRow:(NSInteger)row
  414. {
  415. recordFileModel *model = nil;
  416. if (_audioArr.count > row) {
  417. model = _audioArr[row];
  418. }
  419. else{
  420. return;
  421. }
  422. NSFileManager *fileManager = [NSFileManager defaultManager];
  423. NSString *filePath = model.filePath; // 获取要删除的文件路径
  424. NSError *error = nil;
  425. BOOL success = [fileManager removeItemAtPath:filePath error:&error];
  426. if (!success) {
  427. //NSLog(@"删除文件失败: %@", error.localizedDescription);
  428. [[iToast makeText:NSLocalizedString(@"record_deleteFile_fail",nil)] show];
  429. } else {
  430. //NSLog(@"文件删除成功");
  431. [[iToast makeText:NSLocalizedString(@"delete_file_suc_msg",nil)] show];
  432. [_audioArr removeObject:model];
  433. recordFileModel *firstModel = _audioArr.firstObject;
  434. firstModel.showAllType = YES;
  435. [_tableView reloadData];
  436. }
  437. }
  438. @end