recordViewController.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  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. @interface recordViewController ()<UITableViewDelegate,UITableViewDataSource,DZNEmptyDataSetSource, DZNEmptyDataSetDelegate>
  14. @property (nonatomic, strong) UITableView *tableView;
  15. @property(nonatomic,strong)NSMutableArray *audioArr;//音频数据
  16. @property(nonatomic,strong)recordBottomView *recordBottomV;//底部视图
  17. @property(nonatomic,strong)recordingView *recordingV;//录音中
  18. @end
  19. @implementation recordViewController
  20. - (void)viewDidLoad {
  21. [super viewDidLoad];
  22. // Do any additional setup after loading the view.
  23. _audioArr = [NSMutableArray new];
  24. [self drawAnyView];
  25. [self getRecordDataFun];
  26. }
  27. - (void)drawAnyView
  28. {
  29. [self.view setBackgroundColor:HWF5F7FAColor];
  30. self.navBarBGView.hidden = NO;
  31. [self.navigationBar setHidden:YES];
  32. [self.toolBar setHidden:YES];
  33. [self.titleLabel setText:NSLocalizedString(@"mine_record_title",nil)];
  34. //底部视图
  35. _recordBottomV = [[recordBottomView alloc] init];
  36. [self.view addSubview:_recordBottomV];
  37. [_recordBottomV mas_makeConstraints:^(MASConstraintMaker *make) {
  38. make.left.mas_equalTo(0.f);
  39. make.right.mas_equalTo(0.f);
  40. make.bottom.mas_equalTo(0.f);
  41. make.height.mas_equalTo(100.f + AdaptTabHeight);
  42. }];
  43. KWeakSelf
  44. _recordBottomV.didClickRecordFun = ^{
  45. [weakSelf didClickRecordFun];
  46. };
  47. [self.view addSubview:self.tableView];
  48. [self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
  49. make.left.mas_equalTo(0);
  50. make.right.mas_equalTo(0);
  51. make.bottom.equalTo(self.recordBottomV.mas_top).offset(0.f);
  52. //make.bottom.mas_equalTo(0);
  53. make.top.equalTo(self.navBarBGView.mas_bottom).offset(10.f);
  54. }];
  55. }
  56. #pragma mark - 懒加载
  57. - (UITableView *)tableView{
  58. if (!_tableView) {
  59. _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_W, SCREEN_H - TABBARHEIGHT) style:UITableViewStylePlain];
  60. _tableView.delegate = self;
  61. _tableView.dataSource = self;
  62. _tableView.showsVerticalScrollIndicator = NO;
  63. _tableView.showsHorizontalScrollIndicator = NO;
  64. // _tableView.contentInset = UIEdgeInsetsMake(-H_STATE_BAR, 0, 0, 0);
  65. [_tableView setSeparatorStyle:(UITableViewCellSeparatorStyleNone)];
  66. [_tableView setSeparatorColor:[UIColor clearColor]];
  67. [_tableView setBackgroundColor:[UIColor clearColor]];
  68. [_tableView setTableFooterView:[UIView new]];
  69. [_tableView setBounces:YES];
  70. if (@available(iOS 15.0, *)) {
  71. _tableView.sectionHeaderTopPadding = 0;
  72. }
  73. //空数据引入第三方开源处理
  74. _tableView.emptyDataSetSource = self;
  75. _tableView.emptyDataSetDelegate = self;
  76. // 下拉追加
  77. // MJRefreshAutoNormalFooter *footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
  78. // [self getMoreNetWorkData];
  79. // }];
  80. //
  81. // NSString *text = NSLocalizedString(@"NAS_bottom_tip",nil);
  82. // [footer setTitle:text forState:MJRefreshStateNoMoreData];
  83. // _tableView.mj_footer = footer;
  84. }
  85. return _tableView;
  86. }
  87. #pragma mark - 列表委托
  88. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
  89. if(!_audioArr){
  90. return 0;
  91. }
  92. return _audioArr.count;
  93. }
  94. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  95. return 1;
  96. }
  97. - (recordFileCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
  98. __block NSInteger row = indexPath.section;
  99. static NSString *identifier = @"recordFileCell";
  100. recordFileCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier];
  101. cell.selectionStyle = UITableViewCellSelectionStyleNone;
  102. if (!cell){
  103. cell = [[recordFileCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier];
  104. [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
  105. [cell setBackgroundColor:[UIColor clearColor]];
  106. [cell setAccessoryType:(UITableViewCellAccessoryNone)];
  107. }
  108. if(row < _audioArr.count){
  109. recordFileModel* dataModel = _audioArr[row];
  110. cell.curRecordFileModel = dataModel;
  111. //
  112. // KWeakSelf
  113. // cell.didClickSwitch = ^(BOOL SwitchOn) {
  114. // //if([weakSelf userCheckFileModel:dataModel withShowTip:YES]){
  115. // [weakSelf userCheckFilePreviewByRow:row];
  116. // //}
  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. [_audioArr removeAllObjects];
  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. if (isFirstType) {
  200. model.showAllType = YES;
  201. isFirstType = NO;
  202. }
  203. else{
  204. model.showAllType = NO;
  205. }
  206. // 创建文件信息字典
  207. // NSDictionary *fileInfo = @{
  208. // @"name": fileName,
  209. // @"path": filePath,
  210. // @"size": @(fileSize),
  211. // @"isDirectory": @(isDirectory),
  212. // @"modificationDate": fileAttributes[NSFileModificationDate] ?: [NSDate date]
  213. // };
  214. [_audioArr addObject:model];
  215. }
  216. }
  217. #pragma mark 获取录音文件的时长
  218. - (NSTimeInterval)getDurationOfAudioFile:(NSURL *)fileURL {
  219. NSError *error;
  220. AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:&error];
  221. if (error) {
  222. HLog(@"Error: %@", error.localizedDescription);
  223. return 0;
  224. }
  225. return player.duration;
  226. }
  227. #pragma mark 点击了录音
  228. - (void)didClickRecordFun
  229. {
  230. if(_recordingV){
  231. return;
  232. }
  233. //检测麦克风权限
  234. AVAudioSessionRecordPermission permissionStatus = [[AVAudioSession sharedInstance] recordPermission];
  235. // MicrophonePermissionStatus status;
  236. // switch (permissionStatus) {
  237. // case AVAudioSessionRecordPermissionUndetermined:
  238. // status = MicrophonePermissionStatusNotDetermined;
  239. // break;
  240. // case AVAudioSessionRecordPermissionDenied:
  241. // status = MicrophonePermissionStatusDenied;
  242. // break;
  243. // case AVAudioSessionRecordPermissionGranted:
  244. // status = MicrophonePermissionStatusGranted;
  245. // break;
  246. // }
  247. if (permissionStatus == AVAudioSessionRecordPermissionDenied) {
  248. NSString *tip = NSLocalizedString(@"mine_record_microphone_tip",nil);
  249. [[iToast makeText:tip] show];
  250. return;
  251. }
  252. else if(permissionStatus == AVAudioSessionRecordPermissionUndetermined) {
  253. KWeakSelf
  254. [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
  255. dispatch_async(dispatch_get_main_queue(), ^{
  256. if (granted) {
  257. [weakSelf beginRecordFun];
  258. } else {
  259. }
  260. });
  261. }];
  262. return;
  263. }
  264. [self beginRecordFun];
  265. }
  266. #pragma mark 弹起录音功能
  267. - (void)beginRecordFun
  268. {
  269. _recordingV = [[recordingView alloc] init];
  270. [ksharedAppDelegate.window addSubview:_recordingV];
  271. [_recordingV mas_makeConstraints:^(MASConstraintMaker *make) {
  272. make.left.mas_equalTo(0.f);
  273. make.right.mas_equalTo(0.f);
  274. make.bottom.mas_equalTo(0.f);
  275. make.top.mas_equalTo(0);
  276. }];
  277. NSInteger index = _audioArr.count + 1;
  278. [_recordingV beginRecordFunWith:index];
  279. KWeakSelf
  280. _recordingV.didClickRecordEndFun = ^{
  281. [weakSelf deleteRecordingViewFun];
  282. };
  283. }
  284. #pragma mark 删除录音中界面
  285. - (void)deleteRecordingViewFun
  286. {
  287. [_recordingV removeFromSuperview];
  288. _recordingV = nil;
  289. [self getRecordDataFun];
  290. [self.tableView reloadData];
  291. }
  292. @end