概要
本章主要接到上次的文章讲音频播放,因为音频播放没有对应的播放视图控件,所以本例子自绘了一个简单的音乐播放界面,包括返回、暂停/播放、进度控制等。
结果展示
(暂停/播放 播放进度拖拉 返回)
流程概要
1.接上次的流程新建一个音乐播放类,模拟MPMoviePlayerViewController的实现;
2.播放音频的时使用IOS内置的类AVAudioPlayer,该类支持播放wav.mp3等格式的音乐,其对应的代理类为AVAudioPlayerDelegate;
3.对于这种初始化赋值代码:self._buttonPlayPause = [[UIButton alloc] init];突然想到这种赋值会存在内存泄露 ,因为分配内存直接赋值给属性变量,变量retain两次最后却只release一次。所以以前的好多代码都有问题。汗!!!
4.NSNotificationCenter添加监视时注意object不要为空而要设为nil,不能会导致难发现的内存奔溃问题。
主要代码
h文件
// // MusicPlayerViewController.h // MediaPlayer // // Created by God Lin on 14/12/15. // Copyright (c) 2014年 arbboter. All rights reserved. // #import <UIKit/UIKit.h> #import <AVFoundation/AVFoundation.h> @interface MusicPlayerViewController : UIViewController <AVAudioPlayerDelegate> { AVAudioPlayer* _audioPlayer; UIButton* _buttonPlayPause; UIButton* _buttonReturn; UIImage* _imagePause; UIImage* _imagePlay; UIImage* _imageReturn; UISlider* _sliderPlayer; NSURL* _urlMusic; NSTimer* _timerPlayProcess; BOOL _bPlay; UITextView* _textViewInfo; NSString* _titleMusic; } @property (nonatomic, retain) AVAudioPlayer* _audioPlayer; @property (nonatomic, retain) UIButton* _buttonPlayPause; @property (nonatomic, retain) UIButton* _buttonReturn; @property (nonatomic, retain) UISlider* _sliderPlayer; @property (nonatomic, retain) NSURL* _urlMusic; @property (nonatomic, retain) UIImage* _imagePause; @property (nonatomic, retain) UIImage* _imagePlay; @property (nonatomic, retain) UIImage* _imageReturn; @property (nonatomic, readwrite) BOOL _bPlay; @property (nonatomic, retain) NSTimer* _timerPlayProcess; @property (nonatomic, retain) UITextView* _textViewInfo; @property (nonatomic, retain) NSString* _titleMusic; -(id)initWithContentURL:(NSURL*)url; -(void)setContentURL:(NSURL*)url; @end void AudioFinishedCallBack(SystemSoundID idSound, void* data);
m文件
// // MusicPlayerViewController.m // MediaPlayer // // Created by God Lin on 14/12/15. // Copyright (c) 2014年 arbboter. All rights reserved. // #import "MusicPlayerViewController.h" @interface MusicPlayerViewController () @end @implementation MusicPlayerViewController @synthesize _audioPlayer, _urlMusic; @synthesize _buttonPlayPause, _buttonReturn; @synthesize _sliderPlayer, _titleMusic; @synthesize _imagePause, _imageReturn, _imagePlay; @synthesize _bPlay, _timerPlayProcess, _textViewInfo; // 布局 -(void) doRotateAction:(NSNotification *) notification { [self relayout]; } -(void)relayout { CGFloat x = self.view.frame.origin.x; CGFloat y = self.view.frame.origin.y + 20; CGFloat w = self.view.frame.size.width; CGFloat h = self.view.frame.size.height - 20; CGFloat x1 = 0; CGFloat y1 = 0; CGFloat w1 = 0; CGFloat h1 = 0; UIImage* image = self._imagePause; if(w < h) { image = [UIImage imageNamed:@"oca.jpeg"]; } else { image = [UIImage imageNamed:@"flower.jpg"]; } self.view.backgroundColor = [UIColor colorWithPatternImage:image]; image = self._imageReturn; x1 = x + 5; y1 = y + 5; w1 = image.size.width; h1 = image.size.height; self._buttonReturn.frame = CGRectMake(x1, y1, w1, h1); image = self._imagePause; x1 = x + 5; y1 = (y + h) - (image.size.height + 5); w1 = image.size.width; h1 = image.size.height; self._buttonPlayPause.frame = CGRectMake(x1, y1, w1, h1); x1 += image.size.width ; w1 = x + w - 5 - x1; self._sliderPlayer.frame = CGRectMake(x1, y1, w1, h1); x1 = x + 5; y1 = y + h/4; w1 = w - 10; h1 = h/2; NSArray* fontArray = [UIFont familyNames]; NSString* fontName = [fontArray objectAtIndex:arc4random()%[fontArray count]]; _textViewInfo.font = [UIFont fontWithName:fontName size:h1/10]; _textViewInfo.frame = CGRectMake(x1, y1, w1, h1); _textViewInfo.editable = NO; _textViewInfo.textAlignment = NSTextAlignmentCenter; _textViewInfo.backgroundColor = [UIColor clearColor]; } // 播放界面控件 -(void)onDragPlayProcess { [self._audioPlayer setCurrentTime:self._sliderPlayer.value]; } -(void)onPlayProcess { self._sliderPlayer.value = self._audioPlayer.currentTime; NSInteger nTime = _sliderPlayer.value; NSInteger nMaxTime = _sliderPlayer.maximumValue; NSString* info = [[NSString alloc] initWithFormat:@"%@\n%02ld:%02ld - %02ld:%02ld", _titleMusic, nTime/60, nTime%60, nMaxTime/60, nMaxTime%60]; _textViewInfo.text = info; [info release]; } // 设置播放的文件 -(id)initWithContentURL:(NSURL*)url { self = [super init]; if(self) { [self setContentURL:url]; } return self; } -(void)setContentURL:(NSURL*)url { self._urlMusic = url; } // 启动播放,画面出现前 -(void)viewWillAppear:(BOOL)animated { [self onPlay]; } - (void)viewDidLoad { [super viewDidLoad]; self._imagePlay = [UIImage imageNamed:@"play.png"]; self._imagePause = [UIImage imageNamed:@"pause.png"]; self._imageReturn = [UIImage imageNamed:@"return.png"]; // 突然想到这种赋值会存在内存泄露 -> 分配内存直接赋值给属性变量 // 因为retain两次最后却只release一次,所以以前的好多都有问题 // 汗!!! UIButton* button = nil; button = [[UIButton alloc] init]; self._buttonPlayPause = button; [button release]; [self.view addSubview:self._buttonPlayPause]; [self._buttonPlayPause setImage:self._imagePause forState:UIControlStateNormal]; [self._buttonPlayPause addTarget:self action:@selector(onPlayPause) forControlEvents:UIControlEventTouchUpInside]; button = [[UIButton alloc] init]; self._buttonReturn = button; [button release]; [self.view addSubview:self._buttonReturn]; [self._buttonReturn setImage:self._imageReturn forState:UIControlStateNormal]; [self._buttonReturn addTarget:self action:@selector(onStop) forControlEvents:UIControlEventTouchUpInside]; UISlider* slider = [[UISlider alloc] init]; self._sliderPlayer = slider; [slider release]; [self.view addSubview:self._sliderPlayer]; [self._sliderPlayer addTarget:self action:@selector(onDragPlayProcess) forControlEvents:UIControlEventValueChanged]; UITextView* textView = [[UITextView alloc] init]; self._textViewInfo = textView; [textView release]; [self.view addSubview:_textViewInfo]; [self relayout]; // 切屏 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doRotateAction:) name:UIDeviceOrientationDidChangeNotification object:self]; } -(void)onPlay { if(!_bPlay) { NSError* error; self._bPlay = YES; AVAudioPlayer* player = [[AVAudioPlayer alloc] initWithContentsOfURL:self._urlMusic error:&error]; self._audioPlayer = player; [player release]; self._audioPlayer.delegate = self; self._timerPlayProcess = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(onPlayProcess) userInfo:nil repeats:YES]; //self._audioPlayer; self._sliderPlayer.maximumValue = 0.0f; // 设置播放时长 self._sliderPlayer.maximumValue = self._audioPlayer.duration; [self._audioPlayer play]; // 非法音频 if(_sliderPlayer.maximumValue == 0) { [self onStop]; } [self onPlayProcess]; } } -(void)onStop { [self dismissViewControllerAnimated:YES completion:nil]; [self._audioPlayer stop]; [self._timerPlayProcess invalidate]; _bPlay = NO; } -(void)onPlayPause { UIImage* image = nil; _bPlay = !_bPlay; if(_bPlay) { image = self._imagePause; [self._audioPlayer play]; } else { image = self._imagePlay; [self._audioPlayer pause]; } [self._buttonPlayPause setImage:image forState:UIControlStateNormal]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } -(void)dealloc { [_buttonReturn release]; [_buttonPlayPause release]; [_imageReturn release]; [_imagePause release]; [_imagePlay release]; [_audioPlayer release]; [_urlMusic release]; [_sliderPlayer release]; [[NSNotificationCenter defaultCenter] removeObserver:self]; [super dealloc]; } #pragma mark - AVAudioPlayerDelegate - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag { NSLog(@"Music play over"); [self onStop]; } /* if an error occurs while decoding it will be reported to the delegate. */ - (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error { NSLog(@"Music play occured error"); [self onStop]; } @end // 音频播放回调函数 void AudioFinishedCallBack(SystemSoundID idSound, void* data) { AudioServicesDisposeSystemSoundID(idSound); }
项目工程
时间: 2024-10-11 09:03:24