IOS开发:录音功能,播放功能的完整实现demo
我是在别人的demo基础上继续做的,百度cui ios ,博客园的那个,那人博客写的相当仔细,大家想学的更多可以去他博客看看
原demo只有录音界面,然后录音结束自动播放的功能。
录音界面我稍微改了下,本身是录音结束就自动播放,我是选择了在别的界面播放。然后是取路径命名的问题,我改为了用当前时间命名。原demo是用myRecord.caf作为结尾命名,因为原demo录完就播放嘛。
录音界面有个列表按钮,跳到第二个界面,可看到具体的录音栏目。然后点击右侧的按钮,进入第三个页面自动播放,然后是暂停继续等等
第三个页面自动播放,其实就是把创建音频播放器对象写在了load里,然后调用下音频会话,跟第一个界面的东西基本一样的思路。
然后还有一个关键点是第二个界面点按钮跳转的时候,你必须知道你点的是第几行,也就是你点的是哪个录音,把这个路径送到第三个界面给音频对象用,这个采用单例模式实现跨界面传值。
还有就是页面间的跳转,一种直接拉线,一种是用代码,我的随笔里也写了用代码的。
界面设计
录音界面代码
1 #import "ViewController.h" 2 #import <AVFoundation/AVFoundation.h> 3 4 @interface ViewController ()<AVAudioRecorderDelegate> 5 6 @property (nonatomic,strong) AVAudioRecorder *audioRecorder;//音频录音机 7 @property (nonatomic,strong) AVAudioPlayer *audioPlayer;//音频播放器,用于播放录音文件 8 @property (nonatomic,strong) NSTimer *timer;//录音声波监控(注意这里暂时不对播放进行监控) 9 10 @property (weak, nonatomic) IBOutlet UIButton *record;//开始录音 11 @property (weak, nonatomic) IBOutlet UIButton *pause;//暂停录音 12 @property (weak, nonatomic) IBOutlet UIButton *resume;//恢复录音 13 @property (weak, nonatomic) IBOutlet UIButton *stop;//停止录音 14 @property (weak, nonatomic) IBOutlet UIProgressView *audioPower;//音频波动 15 @property (weak, nonatomic) IBOutlet UIButton *exit;//退出 16 17 @end 18 19 @implementation ViewController 20 21 #pragma mark - 控制器视图方法 22 - (void)viewDidLoad { 23 //f改变progressview的长宽 24 _audioPower.transform=CGAffineTransformMakeScale(1.0f,4.0f); 25 [super viewDidLoad]; 26 //自动播放的 27 [self setAudioSession]; 28 } 29 30 #pragma mark - 私有方法 31 32 33 34 /** 35 * 取得录音文件保存路径 36 * 37 * @return 录音文件路径 38 */ 39 -(NSURL *)getSavePath{ 40 //获取文件路径 41 NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; 42 NSLog(@"%@",urlStr); 43 NSDate *date=[NSDate date]; 44 NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 45 [dateFormatter setDateFormat:@"yyyy-MM-dd-HH:mm:ss"]; 46 //NSDate转NSString 47 NSString *sss = [dateFormatter stringFromDate:date]; 48 NSString *ccc=[sss stringByAppendingString:@".caf"]; 49 urlStr=[urlStr stringByAppendingPathComponent:ccc]; 50 NSURL *url=[NSURL fileURLWithPath:urlStr]; 51 // NSLog(@"第一个页面的路径",url); 52 return url; 53 } 54 55 /** 56 * 取得录音文件设置 57 * 58 * @return 录音设置 59 */ 60 -(NSDictionary *)getAudioSetting{ 61 NSMutableDictionary *dicM=[NSMutableDictionary dictionary]; 62 //设置录音格式 63 [dicM setObject:@(kAudioFormatLinearPCM) forKey:AVFormatIDKey]; 64 //设置录音采样率,8000是电话采样率,对于一般录音已经够了 65 [dicM setObject:@(8000) forKey:AVSampleRateKey]; 66 //设置通道,这里采用单声道 67 [dicM setObject:@(1) forKey:AVNumberOfChannelsKey]; 68 //每个采样点位数,分为8、16、24、32 69 [dicM setObject:@(8) forKey:AVLinearPCMBitDepthKey]; 70 //是否使用浮点数采样 71 [dicM setObject:@(YES) forKey:AVLinearPCMIsFloatKey]; 72 //....其他设置等 73 return dicM; 74 } 75 76 /** 77 * 获得录音机对象 78 * 79 * @return 录音机对象 80 */ 81 -(AVAudioRecorder *)audioRecorder{ 82 if (!_audioRecorder) { 83 //创建录音文件保存路径 84 NSURL *url=[self getSavePath]; 85 //创建录音格式设置 86 NSDictionary *setting=[self getAudioSetting]; 87 //创建录音机 88 NSError *error=nil; 89 _audioRecorder=[[AVAudioRecorder alloc]initWithURL:url settings:setting error:&error]; 90 _audioRecorder.delegate=self; 91 _audioRecorder.meteringEnabled=YES;//如果要监控声波则必须设置为YES 92 if (error) { 93 NSLog(@"创建录音机对象时发生错误,错误信息:%@",error.localizedDescription); 94 return nil; 95 } 96 } 97 return _audioRecorder; 98 } 99 -(void)setAudioSession{ 100 AVAudioSession *audioSession=[AVAudioSession sharedInstance]; 101 //设置为录音状态 102 [audioSession setCategory:AVAudioSessionCategoryRecord error:nil]; 103 [audioSession setActive:YES error:nil]; 104 }/** 105 * 录音声波监控定制器 106 * 107 * @return 定时器 108 */ 109 -(NSTimer *)timer{ 110 if (!_timer) { 111 //创建一个定时器 112 _timer=[NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(audioPowerChange) userInfo:nil repeats:YES]; 113 } 114 return _timer; 115 }; 116 117 /** 118 * 录音声波状态设置 119 */ 120 -(void)audioPowerChange{ 121 [self.audioRecorder updateMeters];//更新测量值 122 float power= [self.audioRecorder averagePowerForChannel:0];//取得第一个通道的音频,注意音频强度范围时-160到0 123 CGFloat progress=(1.0/160.0)*(power+160.0); 124 [self.audioPower setProgress:progress]; 125 }; 126 #pragma mark - UI事件 127 /** 128 * 点击录音按钮 129 * 130 * @param sender 录音按钮 131 */ 132 - (IBAction)recordBu:(UIButton *)sender { 133 if (![self.audioRecorder isRecording]) { 134 [self.audioRecorder record];//首次使用应用时如果调用record方法会询问用户是否允许使用麦克风,需要在info.plist加权限 135 //创建一个定时器 136 self.timer.fireDate=[NSDate distantPast]; 137 } 138 }; 139 140 /** 141 * 点击暂定按钮 142 * 143 * @param sender 暂停按钮 144 */ 145 - (IBAction)pauseBu:(UIButton *)sender { 146 if ([self.audioRecorder isRecording]) { 147 [self.audioRecorder pause]; 148 self.timer.fireDate=[NSDate distantFuture]; 149 NSLog(@"录音暂停"); 150 } 151 }; 152 153 /** 154 * 点击恢复按钮 155 * 恢复录音只需要再次调用record,AVAudioSession会帮助你记录上次录音位置并追加录音 156 * 157 * @param sender 恢复按钮 158 */ 159 - (IBAction)resumeBu:(UIButton *)sender { 160 [self recordBu:sender]; 161 NSLog(@"录音继续"); 162 }; 163 164 /** 165 * 点击停止按钮 166 * 167 * @param sender 停止按钮 168 */ 169 - (IBAction)stopBu:(UIButton *)sender { 170 [self.audioRecorder stop]; 171 self.timer.fireDate=[NSDate distantFuture]; 172 self.audioPower.progress=0.0; 173 NSLog(@"录音文件路径%@",[self getSavePath]); 174 }; 175 /** 176 177 退出 178 */ 179 -(IBAction)exitBu:(UIButton *)sender{ 180 exit(0); 181 }; 182 #pragma mark - 录音机代理方法 183 /** 184 * 录音完成,录音完成后播放录音 185 * 186 * @param recorder 录音机对象 187 * @param flag 是否成功 188 */ 189 /* 190 -(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag{ 191 if (![self.audioPlayer isPlaying]) { 192 [self.audioPlayer play]; 193 } 194 NSLog(@"录音完成!"); 195 }; 196 */ 197 @end
列表界面代码
9 #import "ViewController2.h" 10 #import <Foundation/Foundation.h> 11 #import <AVFoundation/AVFoundation.h> 12 #import "ViewController3.h" 13 #import "Single.h" 14 @interface ViewController2 () <AVAudioRecorderDelegate,UITableViewDelegate,UITableViewDataSource> 15 //,UITableViewDelegate, UITableViewDataSource> 16 //tableView 17 @property (strong, nonatomic) IBOutlet UITableView *recoderTableView; 18 19 @property (strong,nonatomic ) IBOutlet UIButton *remove; 20 21 22 23 24 @end 25 26 27 @implementation ViewController2 28 29 - (void)viewDidLoad { 30 [super viewDidLoad]; 31 self.recoderTableView.dataSource=self; 32 33 } 34 35 //返回数组共下面的方法调用 36 -(NSMutableArray *)getFileList{ 37 NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; 38 NSFileManager *fileManager = [NSFileManager defaultManager]; 39 //将doc下面的文件,以caf为后缀的全部存入数组 40 // NSMutableArray *ss=[fileManager enumeratorAtPath:urlStr]; 41 NSMutableArray *ss=[fileManager contentsOfDirectoryAtPath:urlStr error:nil]; 42 return ss; 43 // NSString *file; 44 /* for(int i=0;i<[ss count];i++) 45 while(file=[ss indexOfObject:i]) 46 { 47 if([[file pathExtension] isEqual:@".caf"]) 48 { 49 [fileList addObject:file]; 50 //看file是否有信息 51 NSLog(@"%@",file); 52 } 53 }*/ 54 } 55 //tableView 的相关方法 56 //告诉控件分为几组 57 -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 58 { 59 return 1; 60 } 61 //告诉UI每组显示几行数据,section参数表示第几组,只有1组,那么section为0 62 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 63 { 64 //一组数据,返回存储文件名称的数组长度 65 return [[self getFileList]count]; 66 } 67 //告诉控件每组的每行显示什么单元格内容 68 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath 69 { 70 UITableViewCell *cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil]; 71 //右侧的按钮 72 cell.accessoryType=UITableViewCellAccessoryDetailButton; 73 74 //获取存了文件信息的数组然后遍历赋值到cell文本中 75 NSMutableArray *ss=[self getFileList]; 76 for(int i=0;i<[tableView numberOfRowsInSection:0];i++) 77 { 78 // indexPath.section==0 79 //控制每行的 80 if( indexPath.row==i) 81 cell.textLabel.text=[ss objectAtIndex:i]; 82 } 83 return cell; 84 85 } 86 #pragma ui控件操作 87 -(IBAction)deleteBU:(UIButton *)sender{ 88 //setedit animate让tableView进入可编辑模式 89 [_recoderTableView setEditing:!_recoderTableView.isEditing animated:true]; 90 } 91 //对tableView自带删除功能的方法实现 92 -(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath 93 { 94 95 if (editingStyle == UITableViewCellEditingStyleDelete) {//删除 96 NSFileManager *fileM=[NSFileManager new]; 97 NSString *newUrl2=[self cellItemPath:indexPath]; 98 [fileM removeItemAtPath:newUrl2 error:nil]; 99 // sing.singleCase=toUrl; 100 [self.recoderTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];//删除刷新 101 } 102 103 } 104 //tableView右侧的点击跳转,需要将URL通过单例对象送到第三个页面 105 - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath 106 { 107 NSString *newUrl2=[self cellItemPath:indexPath]; 108 NSURL *toUrl=[[NSURL alloc]initFileURLWithPath:newUrl2]; 109 NSLog(@"页面二准备复制给单例对象的URL是%@",toUrl); 110 [Single sharedInstance].value=toUrl; 111 //跳转代码 112 UIViewController *next = [[self storyboard] instantiateViewControllerWithIdentifier:@"ViewController3"]; 113 [self presentModalViewController:next animated:NO]; 114 115 } 116 //cell中拿到每行文件的信息,并且拼装成完整的路径,供删除当前行方法,和点击当前行跳转方法使用 117 -(NSString *)cellItemPath:(NSIndexPath *)indexPath{ 118 NSFileManager *fileM=[NSFileManager new]; 119 NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; 120 // NSString *newStr=urlStr+indexPath.row; 121 NSArray *newArr=[fileM contentsOfDirectoryAtPath:urlStr error:nil]; 122 //行数 123 int i=indexPath.row; 124 //接收要删除的元素 125 NSString *newPro; 126 for(int j=0;j<[newArr count];j++) 127 { 128 if(j==i) 129 { 130 newPro= [newArr objectAtIndex:i]; 131 } 132 } 133 //重新拼接路径,路径文件夹名+/+要删除 的文件名 134 NSString *newUrl=[urlStr stringByAppendingString:@"/"]; 135 NSString *newUrl2=[newUrl stringByAppendingString:newPro]; 136 return newUrl2; 137 } 138 139 140 141 @end
播放界面代码
#import "ViewController3.h" #import <AVFoundation/AVFoundation.h> #import "ViewController.h" #import "ViewController2.h" #import "Single.h" @interface ViewController3 ()<AVAudioRecorderDelegate> @property (nonatomic,strong) NSTimer *timer; //声波监控,对播放进行监控 @property (weak, nonatomic) IBOutlet UIProgressView *audioPower;//音频波动 @property (nonatomic,strong) AVAudioPlayer *audioPlayer;//音频播放器,用于播放录音文件 @property (weak,nonatomic ) IBOutlet UIButton *pause;//暂停 @property (weak,nonatomic ) IBOutlet UIButton *conti;//继续 @property (weak,nonatomic ) IBOutlet UIButton *back;//返回 //通过单例对象拿到第二个控制器的cell对象传的URL @property (nonatomic,copy) NSURL *url; @property (weak, nonatomic) IBOutlet UIButton *exit0; @end @implementation ViewController3 - (void)viewDidLoad { [super viewDidLoad]; _url=[Single sharedInstance].value; NSLog(@"页面三%@",_url); if(!_audioPlayer){ _audioPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:_url error:nil]; _audioPlayer.numberOfLoops=0; [_audioPlayer prepareToPlay]; //开启定时器 self.timer.fireDate=[NSDate distantPast]; } [self setAudioSession];
[self.audioPlayer play]; } /** * 设置音频会话 */ -(void)setAudioSession{ AVAudioSession *audioSession=[AVAudioSession sharedInstance]; //设置为播放状态 [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil]; [audioSession setActive:YES error:nil]; } /** * 录音声波监控定制器 * * @return 定时器 */ -(NSTimer *)timer{ if (!_timer) { //创建一个定时器 _timer=[NSTimer scheduledTimerWithTimeInterval:0.5f target:self selector:@selector(audioPowerChange) userInfo:nil repeats:YES]; } return _timer; }; /** * 播放声波状态设置 */ -(void)audioPowerChange{ float progress= self.audioPlayer.currentTime /self.audioPlayer.duration; [self.audioPower setProgress:progress animated:true]; }; #pragma UI操作 //退出 -(IBAction)exitBu:(UIButton *)sender{ exit(0); }; //返回 -(IBAction)backBu:(UIButton *)sender{ UIViewController *next = [[self storyboard] instantiateViewControllerWithIdentifier:@"ViewController2"]; [self presentModalViewController:next animated:NO]; }; //暂停 -(IBAction)pauseBu:(UIButton *)sender { [self.audioPlayer pause]; self.timer.fireDate=[NSDate distantFuture]; }; //继续 -(IBAction)contiBu:(UIButton *)sender{ [self.audioPlayer play]; self.timer.fireDate=[NSDate distantPast]; } @end
还有个单例类存URL的代码,这个我写过了,在随笔里