在 iOS 中使用控件播放音频

Posted

技术标签:

【中文标题】在 iOS 中使用控件播放音频【英文标题】:Playing audio with controls in iOS 【发布时间】:2011-11-23 08:39:02 【问题描述】:

我用tab bar,nav bar and table view 制作了一个应用程序。

在表格视图中,您可以选择收听一些音频。

新视图打开,我有一些控件,例如:播放、暂停、音量滑块、进度滑块、带有当前时间的标签。

它有效,但并不完美。 我可以播放音频,可以暂停音频,也可以使用滑块向前或向后跳。但是现在:

当我点击导航栏上的后退按钮时,歌曲继续播放。没关系,但是当我再次返回视图时,计时器和滑块会自行重置。我无法暂停歌曲,只需等待它停止播放即可。

另外,当我点击播放时,返回表格视图,选择另一个文件播放,第一个文件不会停止播放

这里是 Audio1DetailViewController.h 代码:

     #import <UIKit/UIKit.h>
     #import <AVFoundation/AVFoundation.h>

     @interface Audio1DetailViewController: UIViewController <AVAudioPlayerDelegate> 

     IBOutlet UISlider *volumeControl; 
     IBOutlet UILabel  *timerLabel; 
     IBOutlet UISlider *progressBar; 

     AVAudioPlayer *audioPlayer;
     NSTimer *playbackTimer; 

     

     @property (nonatomic, retain) IBOutlet UISlider *volumeControl;
     @property (nonatomic, retain) IBOutlet UILabel *timerLabel;
     @property (nonatomic, retain) IBOutlet UISlider *progressBar;
     @property (nonatomic, retain) NSTimer  *playbackTimer; 
     @property (nonatomic, retain) AVAudioPlayer *audioPlayer;
     -(IBAction) playAudio;
     -(IBAction) stopAudio;
     -(IBAction) adjustVolume;
     -(IBAction) sliderChanged;

     @end

这里是 Audio1DetailViewController.m 代码:​​

     #import "Audio1DetailViewController.h"


     @implementation Audio1DetailViewController

     @synthesize volumeControl, timerLabel, playbackTimer, progressBar, audioPlayer;

     -(void)playAudio
     
     playbackTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
     target:self
     selector:@selector(updateTime)
     userInfo:nil
     repeats:YES];
     [audioPlayer play];
     
     -(void)stopAudio
     
     [playbackTimer invalidate];
     [audioPlayer stop];
     
     -(void)adjustVolume
     
     if (audioPlayer != nil)
     
     audioPlayer.volume = volumeControl.value;
     
     

     -(void)updateTime
     
     float minutes = floor(audioPlayer.currentTime/60);
     float seconds = audioPlayer.currentTime - (minutes * 60);

     float duration_minutes = floor(audioPlayer.duration/60);
     float duration_seconds = 
     audioPlayer.duration - (duration_minutes * 60);

     NSString *timeInfoString = [[NSString alloc] 
     initWithFormat:@"%0.0f.%0.0f / %0.0f.%0.0f",
     minutes, seconds, 
     duration_minutes, duration_seconds];

     timerLabel.text = timeInfoString;
     [timeInfoString release];
     

     - (void)viewDidLoad 
     [super viewDidLoad];
     NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
     pathForResource:@"001Fatiha"
     ofType:@"MP3"]];

     NSError *error;
     audioPlayer = [[AVAudioPlayer alloc]
     initWithContentsOfURL:url
     error:&error];

     [[AVAudiosession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
     [[AVAudioSession sharedInstance] setActive: YES error: nil];

     if (error)
     
     NSLog(@"Error in audioPlayer: %@", 
     [error localizedDescription]);
      else 
     audioPlayer.delegate = self;
     [audioPlayer prepareToPlay];
     


     playbackTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self
     selector:@selector(updateSlider) userInfo:nil repeats:YES];

     progressBar.maximumValue = audioPlayer.duration;
     // Set the valueChanged target
     [progressBar addTarget:self action:@selector(sliderChanged:) forControlEvents:
     UIControl EventValueChanged];
     

     - (void)updateSlider 
     // Update the slider about the music time
     progressBar.value = audioPlayer.currentTime;
     

     - (IBAction)sliderChanged:(UISlider *)sender 
     // Fast skip the music when user scroll the UISlider
     [audioPlayer stop];
     [audioPlayer setCurrentTime:progressBar.value];
     [audioPlayer prepareToPlay];
     [audioPlayer play];
     

     -(void)audioPlayerDidFinishPlaying:
     (AVAudioPlayer *)player successfully:(BOOL)flag
     
     


     -(void)audioPlayerDecodeErrorDidOccur:
     (AVAudioPlayer *)player error:(NSError *)error
     
     
     -(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player
     
     
     -(void)audioPlayerEndInterruption:(AVAudioPlayer *)player
     
     

     -(void)viewDidUnload 
     audioPlayer = nil;
     volumeControl = nil;

     

     -(void)dealloc 
     [audioPlayer release];
     [volumeControl release];
     [playbackTimer release];
     [super dealloc];
     

     @end

这里是 AudioTableViewController.h

     #import <UIKit/UIKit.h>

     @class Audio1DetailViewController;


     @interface AudioTableViewController : UITableViewController 
     <UITableViewDelegate,UITableViewDataSource>


     IBOutlet UITableView *audioTableView;
     NSMutableArray *audioArray;
     Audio1DetailViewController *audio1DetailViewController;        
     

     @property (nonatomic, retain) NSMutableArray *audioArray;
     @property (nonatomic, retain) Audio1DetailViewController *audio1DetailViewController;

     @end

还有 AudioTableViewController.m

    #import "AudioTableViewController.h"
    #import "Audio1DetailViewController.h"

    #import "DEQAppDelegate.h"

    @implementation AudioTableViewController
    @synthesize audioArray;
    @synthesize audio1DetailViewController;

    - (id)initWithStyle:(UITableViewStyle)style
    
        self = [super initWithStyle:style];
        if (self) 
            // Custom initialization
    
        return self;
    

    - (void)didReceiveMemoryWarning
    
        // Releases the view if it doesn't have a superview.
        [super didReceiveMemoryWarning];

        // Release any cached data, images, etc that aren't in use.
    

    #pragma mark - View lifecycle

    - (void)viewDidLoad
        [super viewDidLoad];
        self.title = NSLocalizedString(@"Audio", @"Audio");
        self.audioArray = [[NSArray alloc] initWithObjects:
                          @"1. Het Begin",  @"2. De Mensen", //etcetera                      
                  nil];
        // Uncomment the following line to preserve selection between presentations.
        self.clearsSelectionOnViewWillAppear = NO;

        // Uncomment the following line to display an Edit button in the navigation bar for this
        view controller.
        // self.navigationItem.rightBarButtonItem = self.editButtonItem;
    

    - (void)viewDidUnload
    
        [super viewDidUnload];
        // Release any retained subviews of the main view.
        self.audioArray = nil;
    

    - (void)viewWillAppear:(BOOL)animated
    
        [super viewWillAppear:animated];
    

    - (void)viewDidAppear:(BOOL)animated
    
        [super viewDidAppear:animated];
    

    - (void)viewWillDisappear:(BOOL)animated
    
        [super viewWillDisappear:animated];
    

    - (void)viewDidDisappear:(BOOL)animated
    
        [super viewDidDisappear:animated];
    

    - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
    
        // Return YES for supported orientations
        return (interfaceOrientation == UIInterfaceOrientationPortrait);
    

    #pragma mark - Table view data source

    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    
    #warning Potentially incomplete method implementation.
        // Return the number of sections.
        return 1;
    

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    
    #warning Incomplete method implementation.
        // Return the number of rows in the section.
        return [self.audioArray count];
    

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
      (NSIndexPath *)indexPath
    
        static NSString *CellIdentifier = @"Cell";

        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        if (cell == nil) 
            cell = [[[UITableViewCell alloc] initWithStyle:
        UITableViewCellStyleDefaultreuseIdentifier:CellIdentifier] autorelease];
        

        // Configure the cell...
        cell.textLabel.text = [self.audioArray objectAtIndex:[indexPath row]];

        return cell;
    

    #pragma mark - Table view delegate

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    
        // Navigation logic may go here. Create and push another view controller.

        NSUInteger row = indexPath.row;

        if (row == 0) 
        
     Audio1DetailViewController *audio1DetailViewController =[[Audio1DetailViewController alloc]
     initWithNibName:@"Audio1DetailViewController" bundle:nil]; 
     audio1DetailViewController.title = @"Audio";
     [self.navigationController pushViewController:audio1DetailViewController animated:YES];   
     [audio1DetailViewController release];

        

        if (row == 1) 
        
            //etcetera
        

    
    - (void)dealloc
        [audioArray release];
        [super dealloc];
    

    @end

【问题讨论】:

,还有See Apple Doc. 【参考方案1】:

我建议您创建一个全局播放器对象。因为现在,每次你在导航控制器上推送这个视图时,你都会创建一个新视图。这也意味着您没有任何对前一个玩家的参考(并且无法阻止它)。所以:声明 AVAudioPlayer 更高一级(在表格视图中)。现在,当您选择其中的一行时,将其分配给这个新视图(您链接的那个)的一个属性。

您使用情节提要吗?然后你必须实现方法prepareForSegue:。在情节提要上为您的 segue 提供一个标识符(例如 showPlayer 并使用 if (segue.identifier isEqualToString:@"showPlayer") 检查)。

现在在您的viewDidLoad 中检查audioPlayer 是否为零。如果你

- (void)viewDidLoad

    [super viewDidLoad];
    if (audioPlayer == nil)
    
         // your init (audioplayer =[[AVAudioPlayer ...
    
    else
    
         if (audioPlayer.isPlaying)
         
             // i.e. pause the player or do other stuff
         
    

希望这对您有所帮助。

另外:请勿发布代码图片。只需在您的回复中插入代码或将其发布到 pastebin.com 之类的网站,然后在您的问题中链接该页面。这使其他人更容易回应并提出如何更改您的代码的建议。

回应您的评论: 相关的东西应该是: 在 AudioTableViewController.h

@property (strong, nonatomic) AVAudioPlayer *audioPlayer;

在 AudioTableViewController.m 中

@synthesize audioPlayer;
...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

    // Navigation logic may go here. Create and push another view controller.

    NSUInteger row = indexPath.row;

    if (row == 0) 
    
        self.audio1DetailViewController =[[Audio1DetailViewController alloc] initWithNibName:@"Audio1DetailViewController" bundle:nil]; 
        self.audio1DetailViewController.title = @"Audio";
        self.audio1DetailViewController.audioPlayer = self.audioPlayer;
        [self.navigationController pushViewController:self.audio1DetailViewController animated:YES];   
        [self.audio1DetailViewController release];
        ...

Audio1DetailViewController.m

- (void)viewDidLoad 
    [super viewDidLoad];
    NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"001Fatiha" ofType:@"MP3"]];

    NSError *error;

    // this is the important part: if we already have something playing, stop it!
    if (audioPlayer != nil)
    
        [audioPlayer stop];
    
    // everything else should happen as normal.
    audioPlayer = [[AVAudioPlayer alloc]
               initWithContentsOfURL:url
               error:&error];

    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
    [[AVAudioSession sharedInstance] setActive: YES error: nil];

【讨论】:

嘿@pxldlx,tnx 的答案。我试过但失败了。你说的听起来很简单,而且有道理。但我就是不明白,我想。我已经从 Audio1DetailViewController.h 在 AudioTableViewController.h 中声明了 AVAudioPlayer 然后我将整个代码(不是 viewDidLoad 部分)从 Audio1DetailViewController.m 移动到 AudioTableViewController.m 我还更改了 xib 文件中的 File'sOwner。我可以播放歌曲,但不是通过按下播放按钮,而是通过移动进度滑块。我尝试了其他一些东西,但 2 天后,我放弃了它并撤消了所有更改。 @iHilas Mh,好的。如果不查看您的代码,我真的无法为您提供太多帮助。您能否编辑您的原始问题并添加一些代码(主要是您已经发布为图像但这次作为文本发布的代码:))。哦,还有一件事:我认为你不必改变那么多。一切都保持不变,但是您将 audioPlayer 的“所有者”更改为该超级视图(从某种意义上说,此控制器具有 strong 属性)。当您遍历该新控制器时,您将参考音频播放器交给新控制器。 仍然没有成功。我已经按照你说的做了,但是它一直在同时播放 2 首歌曲,计时器和滑块也一直在自行重置。我就是想不通!【参考方案2】:

要停止同时播放 2 首歌曲,您应该这样做:

- (void)viewWillDisappear:(BOOL)animated

    audioPlayer = nil;
 volumeControl = nil;

如需更多关于播放音频的帮助,您可以使用苹果示例代码:

http://developer.apple.com/library/ios/#samplecode/avTouch/Introduction/Intro.html

【讨论】:

【参考方案3】:

令人惊讶的是 MPMoviePlayerController 还可以播放带有控件的 MP3 播放器!!!

self.moviePlayer=[[MPMoviePlayerController alloc] initWithContentURL:url];
[self.moviePlayer.view setFrame:CGRectMake(0, 0, self.videoContainer.bounds.size.width, self.videoContainer.bounds.size.height)];
self.moviePlayer.controlStyle=MPMovieControlStyleDefault;
[self.videoContainer addSubview:self.moviePlayer.view];
[self.moviePlayer prepareToPlay];
[self.moviePlayer play];

【讨论】:

这是使用控件处理播放的正确且最直接的方法。在 iOS8 中,Apple 为您显示的控件(播放等)增加了更大的灵活性

以上是关于在 iOS 中使用控件播放音频的主要内容,如果未能解决你的问题,请参考以下文章

IOS开发之简单音频播放器

iOS 媒体播放控件通知

WPF 实现音频播放动画控件

在 iOS5 中自动播放 HTML5 音频/视频

iOS 音频开发经验汇总

iOS音频播放