AVAudioPlayer始终是nill,它的isPlaying属性总是假的。 文件在播放时混合

为了解决这个问题,我已经把头发拉了三天。 我已经检查了很多示例代码,阅读了大量的教程,并且在stackoverflow上search了很多很多的问题和答案,但是我仍然无法解决这个问题。 有这样或那样的几个类似的问题,但他们也没有任何解决scheme。

所以关于我的项目一点点:

我有一个NIKMasterViewController和一个NIKDetailViewController 。 在第一个我有一个表视图中的audio文件列表; select一行,导航到NIKDetailViewController ,用户可以看到关于该文件的一些信息并播放audio文件。 我已经在NIKMasterViewController定义了一个AVAudioPlayer属性,并将其设置为: NIKMasterViewController.h

@property (nonatomic, strong) AVAudioPlayer *audioPlayer;

NIKMasterViewController.m

 @synthesize audioPlayer; - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([[segue identifier] isEqualToString:@"showDetail"]) { NSIndexPath *indexPath = [self.tableView indexPathForCell:sender]; NIKDetailViewController *detailViewController = (NIKDetailViewController *) segue.destinationViewController; [detailViewController setAudioPlayer:audioPlayer]; [detailViewController setFeedEntry:[[[self feedParser] feedItems] objectAtIndex:indexPath.row]]; } else { NSLog(@"Segue Identifier: %@", segue.identifier); } } 

这就是NIKMasterViewControllerAVAudioPlayer 。 现在在我的NIKDetailViewController我有AVAudioPlayer另一个属性:

NIKDetailViewController.h

@property (nonatomic, strong) AVAudioPlayer *audioPlayer;

现在在我的.m文件中,我有一个名为streamAudio的方法,在viewDidLoad调用以准备audio播放,并且我有一个if条件,要求检查audioPlayer是否为nill ,如果不是,如果audioPlayer.isPlaying为true,它stop播放器,但它从来没有被调用,当我回到主VC浏览另一行播放另一个文件,第二个文件开始播放时,第一个文件正在播放,一切都混在一起。

任何帮助都将得到真正的赞赏,因为我几乎要在几个小时和几天之后无法解决这个问题后停止编程!

NIKDetailViewController.m

 @synthesize audioPlayer; - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization selectedItem = [[NSString alloc]init]; } return self; } #pragma mark - Managing the Audio Playback - (IBAction)togglePlayingState:(id)button { //Handle the button pressing [self togglePlayPause]; } - (void)playAudio { //Play the audio and set the button to represent the audio is playing [audioPlayer play]; [playPauseButton setImage:[UIImage imageNamed:@"player_pause"] forState:UIControlStateNormal]; } - (void)pauseAudio { //Pause the audio and set the button to represent the audio is paused [audioPlayer pause]; [playPauseButton setImage:[UIImage imageNamed:@"player_play"] forState:UIControlStateNormal]; } - (void)togglePlayPause { //Toggle if the music is playing or paused if (!audioPlayer.playing) { [self playAudio]; } else if (audioPlayer.playing) { [self pauseAudio]; } } - (void)streamAudio { currentFileName = [[feedEntry podcastDownloadURL] lastPathComponent]; NSString* documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; NSString* path = [documentPath stringByAppendingPathComponent:currentFileName]; NSURL* audioURL = [NSURL fileURLWithPath: path]; if (audioPlayer != nil) { if (audioPlayer.isPlaying) { [audioPlayer stop]; //THIS IS NEVER CALLED } audioPlayer = nil; //THIS IS NEVER CALLED } audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioURL error:nil]; // Set a timer which keep getting the current music time and update the UISlider in 1 sec interval playbackTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateSlider) userInfo:nil repeats:YES]; // Set the maximum value of the UISlider seekSlider.maximumValue = audioPlayer.duration; currentTime.text = [NSString stringWithFormat:@"%d:%02d", (int)audioPlayer.currentTime / 60, (int)audioPlayer.currentTime % 60, nil]; remainingTime.text = [NSString stringWithFormat:@"%d:%02d", (int)(audioPlayer.duration - audioPlayer.currentTime) / 60, (int)(audioPlayer.duration - audioPlayer.currentTime) % 60, nil]; // Set the valueChanged target [seekSlider addTarget:self action:@selector(sliderChanged:) forControlEvents:UIControlEventValueChanged]; audioPlayer.delegate = self; [audioPlayer prepareToPlay]; //Add the audio to the memory. } - (void)updateSlider { // Update the slider about the music time seekSlider.value = audioPlayer.currentTime; } - (IBAction)sliderChanged:(UISlider *)sender { // Fast skip the music when user scrolls the slider [audioPlayer stop]; [audioPlayer setCurrentTime:seekSlider.value]; audioPlayer.delegate = self; [audioPlayer prepareToPlay]; [audioPlayer play]; } // Stop the timer when the music is finished (Need to implement the AVAudioPlayerDelegate in the Controller header) - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag { // Music completed if (flag) { [playbackTimer invalidate]; } } - (IBAction)forwardAudio:(id)sender { int currentTime = [audioPlayer currentTime]; [audioPlayer setCurrentTime:currentTime+10]; } - (IBAction)rewindAudio:(id)sender { int currentTime = [audioPlayer currentTime]; [audioPlayer setCurrentTime:currentTime-10]; } //Make sure we can recieve remote control events - (BOOL)canBecomeFirstResponder { return YES; } - (void)remoteControlReceivedWithEvent:(UIEvent *)event { //if it is a remote control event handle it correctly if (event.type == UIEventTypeRemoteControl) { if (event.subtype == UIEventSubtypeRemoteControlPlay) { [self playAudio]; } else if (event.subtype == UIEventSubtypeRemoteControlPause) { [self pauseAudio]; } else if (event.subtype == UIEventSubtypeRemoteControlTogglePlayPause) { [self togglePlayPause]; } } } #pragma mark - view life cycle - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; //Once the view has loaded then we can register to begin recieving controls and we can become the first responder [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; [self becomeFirstResponder]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; //End recieving events [[UIApplication sharedApplication] endReceivingRemoteControlEvents]; [self resignFirstResponder]; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. [self streamAudio]; //Make sure the system follows our playback status - to support the playback when the app enters the background mode. [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; [[AVAudioSession sharedInstance] setActive: YES error: nil]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end 

注:我已经尝试设置属性在细节VC weak但是,然后,我收到一个警告,属性释放之前,我可以播放文件。

所以…我最终可以通过创build一个audioplayersingleton来解决这个问题。 这是如何:

  1. 首先,我从NIKMasterViewController类中删除了与audioPlayer相关的所有代码,包括audioPlayer声明并将其设置在prepareForSegue
  2. 我创build了一个名为NIKAudioPlayer的新类。
  3. NIKAudioPlayer.h

     #import <Foundation/Foundation.h> #import <AVFoundation/AVFoundation.h> @interface NIKAudioPlayer : NSObject <AVAudioPlayerDelegate> { AVAudioPlayer *currentPlayer; } @property (nonatomic, strong) AVAudioPlayer *currentPlayer; +(NIKAudioPlayer *) sharedPlayer; -(void)playURL:(NSURL*)url; @end 
  4. NIKAudioPlayer.m

     #import "NIKAudioPlayer.h" @implementation NIKAudioPlayer @synthesize currentPlayer; +(NIKAudioPlayer *) sharedPlayer { static NIKAudioPlayer* sharedPlayer; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedPlayer = [[NIKAudioPlayer alloc] init]; }); return sharedPlayer; } -(void)playURL:(NSURL*)url { [currentPlayer stop]; currentPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil]; [currentPlayer prepareToPlay]; } @end 
  5. 现在,在我需要播放audio文件的代码中(在我的情况下,在NIKDetailViewController ),我从NIKAudioPlayer调用NIKAudioPlayer

     [[NIKPlayer sharedPlayer] playURL:audioURL]; [[NIKPlayer sharedPlayer].currentPlayer prepareToPlay]; 

简而言之,将NIKDetailViewController所有audioPlayerreplace为[NIKPlayer sharedPlayer].currentPlayer ,甚至将其转换并在任何地方使用:

audioPlayer = [NIKPlayer sharedPlayer].currentPlayer