且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Objective-C,需要帮助来创建AVAudioPlayer单例

更新时间:2023-11-25 14:28:52

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

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

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

A preview of the player can be seen on ***: http://www.***.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:

背景: