Objective-C,需要帮助来创建AVAudioPlayer单例 [英] Objective-C, Need help creating an AVAudioPlayer singleton

查看:89
本文介绍了Objective-C,需要帮助来创建AVAudioPlayer单例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个音板应用程序,该应用程序有几页按钮来播放声音效果,如果用户希望手动中断剪辑,则每页上都有一个停止按钮.我在每个视图中都使用avaudioplayer在按下该剪辑的按钮时播放声音.在更改视图之前,它可以正常工作.如果用户跳到新页面,则声音会继续播放,并且即使他们返回原始视图,停止按钮也会停止工作.按下声音按钮不再中断正在运行的声音,从而导致两个声音相互重叠.

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单身人士的初学者指南,我将不胜感激.我需要做的就是将文件名传递给共享播放器,并使用声音剪辑按钮开始播放,并且无论用户处于什么视图,都必须使停止按钮发出停止声音.我正在使用启用了情节提要和ARC的ios 5.1 sdk.

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.

推荐答案

在我自己的项目之一中使用的我的解决方案发布在下面.随意复制和粘贴,我打算在项目完成后将其开源:)

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 :)

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

A preview of the player can be seen on 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.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

// 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];
}

资产

对于上面的示例,可以使用以下资产(按钮图像为白色,因此很难在背景下看到):

For the above example, the following assets can be used (button images are white, so hard to see against background):

按钮:

Buttons:

背景:

这篇关于Objective-C,需要帮助来创建AVAudioPlayer单例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆