I'm working on a soundboard app, that has several pages of buttons to play sound effects with a stop button on every page should the user wish to manually interrupt the clip. I'm using avaudioplayer in each view to play the sound upon pressing the button for that clip. It works fine until the view is changed. If a user jumps to a new page the sound keeps playing and the stop button stops working even if they return to the original view. Pressing a sound button no longer interrupts the running sound resulting in two sounds over each other.
我正在开发一个音板应用程序,它有几个页面的按钮,可以在每个页面上播放声音效果,如果用户希望手动中断剪辑的话,可以在每个页面上设置一个停止按钮。我在每个视图中使用avaudioplayer在按下该剪辑的按钮时播放声音。在更改视图之前,它可以正常工作。如果用户跳转到一个新页面,声音会持续播放,即使返回到原始视图,停止按钮也会停止工作。按下一个声音按钮将不再中断正在运行的声音,从而导致两个声音相互重叠。
From googling and searching this site, I know the issue is that each view change creates a new instance of the player and the remedy is to create a singleton class. Unfortunately I have yet to find any further examples of how to actually do this. If someone could provide or point the way to a beginners guide for creating an avaudioplayer singleton I would really appreciate it. All I need to be able to do is pass the file name to the shared player and start play with a sound clip button and have the stop button stop sounds no matter what view the user is on. I am using the ios 5.1 sdk with storyboards and ARC enabled.
通过google和搜索这个站点,我知道问题是每个视图更改都会创建一个播放器的新实例,而补救方法是创建一个单例类。不幸的是,我还没有找到进一步的例子来说明如何实现这一点。如果有人能提供或指出创建avaudioplayer单例的初学者指南,我将非常感激。我所需要做的就是将文件名传递给共享播放器,并使用一个声音剪辑按钮开始播放,无论用户使用的是什么视图,都有停止按钮停止声音。我正在使用ios 5.1 sdk,支持故事板和ARC。
2 个解决方案
#1
10
My solution, as used in one of my own projects, is posted beneath. Feel free to copy-and-paste, I intend to open-source this project once it's finished :)
我的解决方案,正如我自己的一个项目中所使用的,贴在下面。请随意复制粘贴,我打算在项目完成后开源这个项目:)
A preview of the player can be seen on YouTube: http://www.youtube.com/watch?v=Q98DQ6iNTYM
该播放器的预览可以在YouTube上看到:http://www.youtube.com/watch?v=Q98DQ6iNTYM
AudioPlayer.h
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
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
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
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
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.m
AudioPlayerView.m
@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
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];
From any view controller inside the app I start audio with the following code:
在app内的任何视图控制器中,我用以下代码启动音频:
- (void)playAudioWithURL:(NSURL *)URL title:(NSString *)title
{
OnsNieuwsAppDelegate *appDelegate = (OnsNieuwsAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.audioPlayer playAudioAtURL:URL withTitle:title];
}
Assets
资产
For the above example, the following assets can be used (button images are white, so hard to see against background):
对于上面的示例,可以使用以下资产(按钮图像是白色的,背景很难看到):
Buttons:
按钮:
Background:
背景:
#2
2
There's a lot of discussion (and links to blogs, etc.) about singletons over at What should my Objective-C singleton look like?, and I see a fair number of tutorials as a result of this Google search: http://www.google.com/search?q=+cocoa+touch+singleton+tutorial, but the real answer to your question, I believe, is that you should do one of two things:
有很多关于单例的讨论(和博客链接等)关于我的Objective-C singleton是什么样子的?在这个谷歌搜索的结果中,我看到了相当多的教程:http://www.google.com/search?q=+cocoa+touch+singleton+教程,但我相信你的问题的真正答案是,你应该做以下两件事中的一件:
If you do want the sound for a particular view to continue playing when the user switches, create the player as you're doing now, but when the view (re)appears, check that a player exists, and don't make a new one.
如果您确实想要特定视图的声音在用户切换时继续播放,就像您现在所做的那样创建播放器,但是当视图(re)出现时,检查一个播放器是否存在,不要创建一个新的播放器。
If you want the sound to stop, then stop the sound when the view changes (i.e., in viewWillDisappear:
).
如果你想让声音停止,那么在视图改变时停止声音。viewWillDisappear:)。
#1
10
My solution, as used in one of my own projects, is posted beneath. Feel free to copy-and-paste, I intend to open-source this project once it's finished :)
我的解决方案,正如我自己的一个项目中所使用的,贴在下面。请随意复制粘贴,我打算在项目完成后开源这个项目:)
A preview of the player can be seen on YouTube: http://www.youtube.com/watch?v=Q98DQ6iNTYM
该播放器的预览可以在YouTube上看到:http://www.youtube.com/watch?v=Q98DQ6iNTYM
AudioPlayer.h
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
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
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
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
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.m
AudioPlayerView.m
@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
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];
From any view controller inside the app I start audio with the following code:
在app内的任何视图控制器中,我用以下代码启动音频:
- (void)playAudioWithURL:(NSURL *)URL title:(NSString *)title
{
OnsNieuwsAppDelegate *appDelegate = (OnsNieuwsAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.audioPlayer playAudioAtURL:URL withTitle:title];
}
Assets
资产
For the above example, the following assets can be used (button images are white, so hard to see against background):
对于上面的示例,可以使用以下资产(按钮图像是白色的,背景很难看到):
Buttons:
按钮:
Background:
背景:
#2
2
There's a lot of discussion (and links to blogs, etc.) about singletons over at What should my Objective-C singleton look like?, and I see a fair number of tutorials as a result of this Google search: http://www.google.com/search?q=+cocoa+touch+singleton+tutorial, but the real answer to your question, I believe, is that you should do one of two things:
有很多关于单例的讨论(和博客链接等)关于我的Objective-C singleton是什么样子的?在这个谷歌搜索的结果中,我看到了相当多的教程:http://www.google.com/search?q=+cocoa+touch+singleton+教程,但我相信你的问题的真正答案是,你应该做以下两件事中的一件:
If you do want the sound for a particular view to continue playing when the user switches, create the player as you're doing now, but when the view (re)appears, check that a player exists, and don't make a new one.
如果您确实想要特定视图的声音在用户切换时继续播放,就像您现在所做的那样创建播放器,但是当视图(re)出现时,检查一个播放器是否存在,不要创建一个新的播放器。
If you want the sound to stop, then stop the sound when the view changes (i.e., in viewWillDisappear:
).
如果你想让声音停止,那么在视图改变时停止声音。viewWillDisappear:)。