2012-03-26 25 views
3

我正在制作一个soundboard应用程序,该应用程序有几页按钮,可以在用户希望手动中断剪辑时在每个页面上播放带有停止按钮的声音效果。我在每个视图中使用avaudioplayer在按下该剪辑的按钮时播放声音。它工作正常,直到视图改变。如果用户跳转到新页面,则声音会继续播放,即使返回原始视图,停止按钮也会停止工作。按下一个声音按钮不会中断运行的声音,从而导致彼此发出两个声音。Objective-C,需要帮助创建AVAudioPlayer单身人士

从谷歌搜索和搜索这个网站,我知道问题是,每个视图更改创建一个新的玩家实例,补救措施是创建一个单身人士课程。不幸的是我还没有找到任何进一步的例子来说明如何实际做到这一点。如果有人可以提供或指向创建avaudioplayer单身人士的初学者指南,我将非常感激。我需要做的只是将文件名传递给共享播放器,然后使用声音片段按钮开始播放,并且不管用户在哪个视图上都停止播放声音。我正在使用ios 5.1 sdk与故事板和ARC启用。

回答

2

What should my Objective-C singleton look like?有很多关于单身人士的讨论(以及博客等的链接),并且我看到相当数量的教程是该Google搜索的结果:http://www.google.com/search?q=+cocoa+touch+singleton+tutorial,但是对于您的问题的真实答案,我认为,这是你应该做的两件事情之一:

如果想要的声音特定视图继续播放时,用户交换机,创建作为你现在在做球员,但是当查看(重新)出现,请检查玩家是否存在,并且不要制作新玩家。

如果您希望声音停止,那么会在视图更改时停止声音(即,在viewWillDisappear:中)。

+0

如果视图改变,我确实希望声音能够继续播放,但即使在返回视图时查看播放器是否已存在,停止按钮不再停止正在播放的声音。停止按钮的代码只是[audioplayer stop]:。看起来,在回到原始视图时,[听觉停止]不再与该特定实例相关联。 – 2012-03-26 19:43:27

10

我的解决方案,在我自己的项目中使用,发布在下方。随意复制和粘贴,我打算开源这个项目一旦它完成:)

播放器的预览可以在YouTube上看到:http://www.youtube.com/watch?v=Q98DQ6iNTYM

AudioPlayer.h

@protocol AudioPlayerDelegate; 

@interface AudioPlayer : NSObject 

@property (nonatomic, assign, readonly) BOOL isPlaying; 
@property (nonatomic, assign) id <AudioPlayerDelegate> delegate; 

+ (AudioPlayer *)sharedAudioPlayer; 

- (void)playAudioAtURL:(NSURL *)URL; 
- (void)play; 
- (void)pause; 

@end 



@protocol AudioPlayerDelegate <NSObject> 
@optional 
- (void)audioPlayerDidStartPlaying; 
- (void)audioPlayerDidStartBuffering; 
- (void)audioPlayerDidPause; 
- (void)audioPlayerDidFinishPlaying; 
@end 

AudioPlayer.m

// import AVPlayer.h & AVPlayerItem.h 


@interface AudioPlayer() 
- (void)playerItemDidFinishPlaying:(id)sender; 
@end 


@implementation AudioPlayer 
{ 
    AVPlayer *player; 
} 

@synthesize isPlaying, delegate; 

+ (AudioPlayer *)sharedAudioPlayer 
{ 
    static dispatch_once_t pred; 
    static AudioPlayer *sharedAudioPlayer = nil; 
    dispatch_once(&pred,^
    { 
     sharedAudioPlayer = [[self alloc] init]; 

     [[NSNotificationCenter defaultCenter] addObserver:sharedAudioPlayer selector:@selector(playerItemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; 
    }); 
    return sharedAudioPlayer; 
} 

- (void)playAudioAtURL:(NSURL *)URL 
{ 
    if (player) 
    { 
     [player removeObserver:self forKeyPath:@"status"]; 
     [player pause]; 
    } 

    player = [AVPlayer playerWithURL:URL]; 
    [player addObserver:self forKeyPath:@"status" options:0 context:nil]; 

    if (delegate && [delegate respondsToSelector:@selector(audioPlayerDidStartBuffering)]) 
     [delegate audioPlayerDidStartBuffering]; 
} 

- (void)play 
{ 
    if (player) 
    { 
     [player play]; 

     if (delegate && [delegate respondsToSelector:@selector(audioPlayerDidStartPlaying)]) 
      [delegate audioPlayerDidStartPlaying]; 
    } 
} 

- (void)pause 
{ 
    if (player) 
    { 
     [player pause]; 

     if (delegate && [delegate respondsToSelector:@selector(audioPlayerDidPause)]) 
      [delegate audioPlayerDidPause]; 
    } 
} 

- (BOOL)isPlaying 
{ 
    DLog(@"%f", player.rate); 

    return (player.rate > 0); 
} 

#pragma mark - AV player 

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 
{ 
    if (object == player && [keyPath isEqualToString:@"status"]) 
    { 
     if (player.status == AVPlayerStatusReadyToPlay) 
     { 
      [self play]; 
     } 
    } 
} 

#pragma mark - Private methods 

- (void)playerItemDidFinishPlaying:(id)sender 
{ 
    DLog(@"%@", sender); 

    if (delegate && [delegate respondsToSelector:@selector(audioPlayerDidFinishPlaying)]) 
     [delegate audioPlayerDidFinishPlaying]; 
} 

@end 

AudioPlayerViewController.h

extern NSString *const kAudioPlayerWillShowNotification; 
extern NSString *const kAudioPlayerWillHideNotification; 


@interface AudioPlayerViewController : UIViewController 

@property (nonatomic, assign, readonly) BOOL isPlaying; 
@property (nonatomic, assign, readonly) BOOL isPlayerVisible; 

- (void)playAudioAtURL:(NSURL *)URL withTitle:(NSString *)title; 
- (void)pause; 

@end 

AudioPlayerViewController.m

NSString *const kAudioPlayerWillShowNotification = @"kAudioPlayerWillShowNotification"; 
NSString *const kAudioPlayerWillHideNotification = @"kAudioPlayerWillHideNotification"; 


@interface AudioPlayerViewController() <AudioPlayerDelegate> 

@property (nonatomic, strong) AudioPlayerView *playerView; 

- (void)playButtonTouched:(id)sender; 
- (void)closeButtonTouched:(id)sender; 
- (void)hidePlayer; 

@end 


@implementation AudioPlayerViewController 

@synthesize playerView, isPlaying, isPlayerVisible; 

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 
{ 
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 
    if (self) 
    { 
     playerView = [[AudioPlayerView alloc] initWithFrame:CGRectZero]; 

     [AudioPlayer sharedAudioPlayer].delegate = self; 
    } 
    return self; 
} 

- (void)didReceiveMemoryWarning 
{ 
    [super didReceiveMemoryWarning]; 
} 

#pragma mark - View lifecycle 

// Implement loadView to create a view hierarchy programmatically, without using a nib. 
- (void)loadView 
{ 
    self.view = playerView; 
} 


// Implement viewDidLoad to do additional setup after loading the view, typically from a nib. 
- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    [playerView.playButton addTarget:self action:@selector(playButtonTouched:) forControlEvents:UIControlEventTouchUpInside]; 
    [playerView.closeButton addTarget:self action:@selector(closeButtonTouched:) forControlEvents:UIControlEventTouchUpInside]; 
} 

- (void)viewDidUnload 
{ 
    [super viewDidUnload]; 
} 

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

#pragma mark - Private methods 

- (AudioPlayerView *)playerView 
{ 
    return (AudioPlayerView *)self.view; 
} 

- (void)hidePlayer 
{ 
    [[NSNotificationCenter defaultCenter] postNotificationName:kAudioPlayerWillHideNotification object:nil]; 
    [self.playerView hidePlayer]; 
} 

- (void)playButtonTouched:(id)sender 
{ 
    DLog(@"play/pause"); 

    if ([AudioPlayer sharedAudioPlayer].isPlaying) 
    { 
     [[AudioPlayer sharedAudioPlayer] pause]; 
    } 
    else 
    { 
     [[AudioPlayer sharedAudioPlayer] play]; 
    } 

    [self.playerView showPlayer]; 
} 

- (void)closeButtonTouched:(id)sender 
{ 
    DLog(@"close"); 

    if ([AudioPlayer sharedAudioPlayer].isPlaying) 
     [[AudioPlayer sharedAudioPlayer] pause]; 

    [self hidePlayer]; 
} 

#pragma mark - Instance methods 

- (void)playAudioAtURL:(NSURL *)URL withTitle:(NSString *)title 
{ 
    playerView.titleLabel.text = title; 
    [[AudioPlayer sharedAudioPlayer] playAudioAtURL:URL]; 

    [[NSNotificationCenter defaultCenter] postNotificationName:kAudioPlayerWillShowNotification object:nil]; 
    [playerView showPlayer]; 
} 

- (void)pause 
{ 
    [[AudioPlayer sharedAudioPlayer] pause]; 

    [[NSNotificationCenter defaultCenter] postNotificationName:kAudioPlayerWillHideNotification object:nil]; 
    [playerView hidePlayer]; 
} 

#pragma mark - Audio player delegate 

- (void)audioPlayerDidStartPlaying 
{ 
    DLog(@"did start playing"); 

    playerView.playButtonStyle = PlayButtonStylePause;  
} 

- (void)audioPlayerDidStartBuffering 
{ 
    DLog(@"did start buffering"); 

    playerView.playButtonStyle = PlayButtonStyleActivity; 
} 

- (void)audioPlayerDidPause 
{ 
    DLog(@"did pause"); 

    playerView.playButtonStyle = PlayButtonStylePlay; 
} 

- (void)audioPlayerDidFinishPlaying 
{ 
    [self hidePlayer]; 
} 

#pragma mark - Properties 

- (BOOL)isPlaying 
{ 
    return [AudioPlayer sharedAudioPlayer].isPlaying; 
} 

- (BOOL)isPlayerVisible 
{ 
    return !playerView.isPlayerHidden; 
} 

@end 

AudioPlayerView.h

typedef enum 
{ 
    PlayButtonStylePlay = 0, 
    PlayButtonStylePause, 
    PlayButtonStyleActivity, 
} PlayButtonStyle; 


@interface AudioPlayerView : UIView 

@property (nonatomic, strong) UIButton    *playButton; 
@property (nonatomic, strong) UIButton    *closeButton; 
@property (nonatomic, strong) UILabel     *titleLabel; 
@property (nonatomic, strong) UIActivityIndicatorView *activityView; 
@property (nonatomic, assign) PlayButtonStyle   playButtonStyle; 
@property (nonatomic, assign, readonly) BOOL   isPlayerHidden; 

- (void)showPlayer; 
- (void)hidePlayer; 

@end 

AudioPlayerView。米

@implementation AudioPlayerView 
{ 
    BOOL _isAnimating; 
} 

@synthesize playButton, closeButton, titleLabel, playButtonStyle, activityView, isPlayerHidden = _playerHidden; 

- (id)initWithFrame:(CGRect)frame 
{ 
    self = [super initWithFrame:frame]; 
    if (self) 
    { 
     self.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"musicplayer_background.png"]]; 

     _playerHidden = YES; 

     activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];   
     activityView.frame = CGRectMake(0.0f, 0.0f, 30.0f, 30.0f); 
     [self addSubview:activityView]; 

     playButton = [[UIButton alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 30.0f, 30.0f)]; 
     [playButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 
     [playButton setBackgroundImage:[UIImage imageNamed:@"button_pause.png"] forState:UIControlStateNormal]; 
     playButton.titleLabel.textAlignment = UITextAlignmentCenter; 
     [self addSubview:playButton]; 

     closeButton = [[UIButton alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 30.0f, 30.0f)]; 
     [closeButton setBackgroundImage:[UIImage imageNamed:@"button_close.png"] forState:UIControlStateNormal]; 
     [closeButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 
     closeButton.titleLabel.textAlignment = UITextAlignmentCenter; 
     [self addSubview:closeButton];   

     titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 240.0f, 30.0f)]; 
     titleLabel.text = nil; 
     titleLabel.textAlignment = UITextAlignmentCenter; 
     titleLabel.font = [UIFont boldSystemFontOfSize:13.0f]; 
     titleLabel.numberOfLines = 2; 
     titleLabel.textColor = [UIColor whiteColor]; 
     titleLabel.backgroundColor = [UIColor clearColor]; 
     [self addSubview:titleLabel]; 
    } 
    return self; 
} 

- (void)layoutSubviews 
{  

#define PADDING 5.0f 

    DLog(@"%@", NSStringFromCGRect(self.bounds)); 
    CGRect frame = self.bounds; 
    CGFloat y = frame.size.height/2; 

    titleLabel.center = CGPointMake(frame.size.width/2, y); 

    CGFloat x = titleLabel.frame.origin.x - (playButton.frame.size.width/2) - PADDING; 
    playButton.center = CGPointMake(x, y); 
    activityView.center = CGPointMake(x, y); 

    x = titleLabel.frame.origin.x + titleLabel.frame.size.width + (closeButton.frame.size.width/2) + PADDING; 
    closeButton.center = CGPointMake(x, y); 
} 

#pragma mark - Instance methods 

- (void)showPlayer 
{ 
    if (_isAnimating || _playerHidden == NO) 
     return; 

    _isAnimating = YES; 

    [UIView 
    animateWithDuration:0.5f 
    animations:^ 
    { 
     CGRect frame = self.frame; 
     frame.origin.y -= 40.0f; 
     self.frame = frame;   
    } 
    completion:^ (BOOL finished) 
    { 
     _isAnimating = NO; 
     _playerHidden = NO;  
    }]; 
} 

- (void)hidePlayer 
{ 
    if (_isAnimating || _playerHidden) 
     return; 

    _isAnimating = YES; 

    [UIView 
    animateWithDuration:0.5f 
    animations:^ 
    {   
     CGRect frame = self.frame; 
     frame.origin.y += 40.0f; 
     self.frame = frame; 
    } 
    completion:^ (BOOL finished) 
    { 
     _isAnimating = NO; 
     _playerHidden = YES;  
    }]; 
} 

- (void)setPlayButtonStyle:(PlayButtonStyle)style 
{ 
    playButton.hidden = (style == PlayButtonStyleActivity); 
    activityView.hidden = (style != PlayButtonStyleActivity); 

    switch (style) 
    { 
     case PlayButtonStyleActivity: 
     { 
      [activityView startAnimating]; 
     } 
      break; 
     case PlayButtonStylePause: 
     { 
      [activityView stopAnimating]; 

      [playButton setBackgroundImage:[UIImage imageNamed:@"button_pause.png"] forState:UIControlStateNormal]; 
     } 
      break; 
     case PlayButtonStylePlay: 
     default: 
     { 
      [activityView stopAnimating]; 

      [playButton setBackgroundImage:[UIImage imageNamed:@"button_play.png"] forState:UIControlStateNormal]; 
     } 
      break; 
    } 

    [self setNeedsLayout]; 
} 

@end 

的AppDelegate - didFinishLaunching

// setup audio player 

audioPlayer = [[AudioPlayerViewController alloc] init]; // public property ... 
CGRect frame = self.window.rootViewController.view.frame; 
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController; 
CGFloat tabBarHeight = tabBarController.tabBar.frame.size.height; 
audioPlayer.view.frame = CGRectMake(0.0f, frame.size.height - tabBarHeight, 320.0f, 40.0f); 
[self.window.rootViewController.view insertSubview:audioPlayer.view belowSubview:tabBarController.tabBar]; 

从应用内的我开始用下面的代码音频任何视图控制器:

- (void)playAudioWithURL:(NSURL *)URL title:(NSString *)title 
{ 
    OnsNieuwsAppDelegate *appDelegate = (OnsNieuwsAppDelegate *)[[UIApplication sharedApplication] delegate]; 
    [appDelegate.audioPlayer playAudioAtURL:URL withTitle:title]; 
} 

资产

对于上面的例子,下面的资产可以使用(按钮图像是白色的,所以很难看到针对背景):

按钮:Close Pause Play

背景:Background

+0

完美!我爱你! – duci9y 2013-01-31 16:11:42

+0

超级!这是一个很大的帮助。我一直在乱搞我的代码没有取得任何成功,终于可以找到这个链接,你的代码工作得很好。但是我需要重新实现我的其他玩家,但这应该没问题。非常感谢! P.S.我编辑了'UITextAlignmentCenter'到'NSTextAlignmentCenter',因为前者现在已被弃用。 – Neeku 2013-11-25 19:55:22

+0

@沃尔夫冈我知道这可能太多了,但是您是否已经实施了一种方法来使查找滑块显示音频进度并使用户能够更新它?如果是这样,你能分享一下吗? 我做了一个不错的'AVAudioPlayer',但由于我无法获得一个单身人士的工作,所以我花了几天的时间就实现了你的代码。现在你的代码完美地工作,但我无法得到它的滑块工作。 – Neeku 2013-11-26 10:32:57