• iOS音乐播放封装(锁屏后台播放,歌曲切换)


    使用时:可以传入一个播放队列,他会自动列表循环,也可以在外部自己管理播放队列,每次传入一个item,监听到是切换,传入自定义列表的下一个item

    注意,需要锁屏后台播放需在项目设置

     具体代码
    MusicsPlayer.h

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface MusicPlayerInfo : NSObject
    @property(nonatomic,strong) NSString * filePath;//歌曲路径(可以是本地路径,也可以是网络路径)
    @property(nonatomic,strong) NSString * name;//歌曲名称
    @property(nonatomic,strong) NSString * playerName;//歌手名称
    @property(nonatomic,strong) NSString * picPath;//歌曲图片路径
    @property(nonatomic,assign) BOOL needBack;//是否后台播放
    @property(nonatomic,assign) NSInteger slider;//进度,已经播放多少秒
    @property(nonatomic,assign) BOOL isplay;// 是否正在播放
    @end
    
    
    @interface MusicsPlayer : NSObject
    +(instancetype) sharedInstance;
    
    //设置播放列表//默认会播放第一首
    //alltime 多少秒后结束播放,,大于0则会定时关闭 小于0不会定时关闭
    //completion 播放状态改变的回调通知 ischange 是否切换歌曲  ischange=-1 暂停 ; ischange=1 播放 ; ischange=-2 上一首 ; ischange=2 下一首
    -(void)playMusics:(NSArray<MusicPlayerInfo*> *)musicInfos endTime:(NSInteger)alltime stateChange:(void (^)(int ischange))completion;
    
    //更改当前音乐的播放进度,从多少秒开始播放(time 秒)
    -(void)playMusicWithSlider:(NSInteger)time;
    
    //停止//会直接销毁
    -(void)stopMusic;
    
    //继续//上次播放
    -(void)continuePlayMusic;
    
    //暂停//后面可以继续播放
    -(void)pauseMusic;
    
    //下一首
    -(void)nextMusic;
    
    //上一首
    -(void)upMusic;
    
    //获取当前播放信息
    -(MusicPlayerInfo *)playingMusicInfo;
    //回调
    //通知前端播放状态改变,///前端可以调用playingMusicInfo获取最新信息
    
    +(void)applicationWillEnterForeground;
    +(void)applicationDidBecomeActive;
    @end
    
    NS_ASSUME_NONNULL_END

    MusicsPlayer.m

    #import "MusicsPlayer.h"
    #import <AVKit/AVKit.h>
    #import <MediaPlayer/MediaPlayer.h>
    
    
    @interface MusicsPlayer ()
    
    @property (nonatomic,strong)AVPlayer  *  musicPlayer;
    @property (nonatomic,strong)NSArray<MusicPlayerInfo*> * musicInfos;
    @property(nonatomic,assign)NSInteger playingIndex;//当前播放歌曲索引
    @property(nonatomic,assign)BOOL isplaying;//是否正在播放
    @property (copy, nonatomic) void (^playerChangeBlock)(int ischange);//播放器状态改变回调
    @property(nonatomic,strong) NSTimer * changeTimer;//自动关闭计时器
    @property(nonatomic,assign)NSInteger endPlayTime;//停止播放倒计时
    @property(nonatomic,assign)BOOL isBackPlayer;//是否是后台播放
    
    @end
    
    @implementation MusicsPlayer
    
    static MusicsPlayer * _instance = nil;
    
    +(instancetype) sharedInstance{
       static dispatch_once_t onceToken ;
       dispatch_once(&onceToken, ^{
           _instance = [[self alloc]init];
       }) ;
       return _instance ;
    }
    -(instancetype)init{
       self = [super init];
       if(self){
           [self loadBackFunction];
       }
       return self;
    }
    
    -(void)loadBackFunction{
       AVAudioSession  *session  =  [AVAudioSession  sharedInstance];
       [session setActive:YES error:nil];
       [session setCategory:AVAudioSessionCategoryPlayback error:nil];
       // *让app接受远程事件控制,及锁屏是控制版会出现播放按钮
       [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
       
       MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
       
       MPRemoteCommand *playCommand = commandCenter.playCommand;
       playCommand.enabled = YES;
       [playCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
           NSLog(@"点击播放");
           [self continuePlayMusic];
           return MPRemoteCommandHandlerStatusSuccess;
       }];
       MPRemoteCommand *pauseCommand = commandCenter.pauseCommand;
       pauseCommand.enabled = YES;
       [pauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
           NSLog(@"点击暂停");
           [self pauseMusic];
           return MPRemoteCommandHandlerStatusSuccess;
       }];
       MPRemoteCommand *nextTrackCommand = commandCenter.nextTrackCommand;
       nextTrackCommand.enabled = YES;
       [nextTrackCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
           NSLog(@"点击下一首");
           [self nextMusic];
           return MPRemoteCommandHandlerStatusSuccess;
       }];
       MPRemoteCommand *previousTrackCommand = commandCenter.previousTrackCommand;
       previousTrackCommand.enabled = YES;
       [previousTrackCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
           NSLog(@"点击上一首");
           [self upMusic];
           return MPRemoteCommandHandlerStatusSuccess;
       }];
       
       [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(endOfPlay:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
    }
    
    -(void)playMusic{
       MusicPlayerInfo * musicInfo = self.musicInfos[self.playingIndex];
       NSString * filePath = musicInfo.filePath;
       self.isBackPlayer = musicInfo.needBack;
       
       if(self.musicPlayer){
           [self.musicPlayer.currentItem removeObserver:self forKeyPath:@"status"];//老的item移除监听
       }else{
           self.musicPlayer = [[AVPlayer alloc] init];
       }
       NSURL * url ;
       if([filePath containsString:@"http"]){
           url = [NSURL URLWithString:filePath];
       }else{
           url = [NSURL fileURLWithPath:filePath];
       }
       AVPlayerItem * item = [[AVPlayerItem alloc] initWithURL:url];
       // 为item的status添加观察者.
       [item addObserver:self forKeyPath:@"status" options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) context:nil];
       // 用新创建的item,替换AVPlayer之前的item.新的item是带着观察者的哦.
       [self.musicPlayer replaceCurrentItemWithPlayerItem:item];
    }
    
    -(void)loadLockViewInfo{
       MusicPlayerInfo * musicInfo = self.musicInfos[self.playingIndex];
       NSString * name = musicInfo.name;
       NSString * playerName = musicInfo.playerName;
       NSString * picPath = musicInfo.picPath;
       //设置锁屏状态下屏幕显示音乐信息
       //设置后台播放时显示的东西,例如歌曲名字,图片等
       UIImage *image;
       if([picPath containsString:@"http"]){
           NSData * imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:picPath]];
           image = [UIImage imageWithData:imageData];
       }else{
           image = [UIImage imageWithContentsOfFile:picPath];
       }
       MPMediaItemArtwork *artWork ;
       if ([UIDevice currentDevice].systemVersion.floatValue >= 10.0) {
           artWork = [[MPMediaItemArtwork alloc] initWithBoundsSize:CGSizeMake(512, 512) requestHandler:^UIImage * _Nonnull(CGSize size) {
               return image;
           }];
       }else{
            artWork = [[MPMediaItemArtwork alloc] initWithImage:image];
       }
       NSDictionary *dic = @{MPMediaItemPropertyTitle:name,
                             MPMediaItemPropertyArtist:playerName,
                             MPMediaItemPropertyArtwork:artWork,
                             MPNowPlayingInfoPropertyElapsedPlaybackTime:@([self playingSlider]),
                             MPNowPlayingInfoPropertyPlaybackRate:@(1.0),
                             MPMediaItemPropertyPlaybackDuration:@([self musicDuration]),
                             };
       [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:dic];
       
    }
    
    // 播放结束后的方法,
    -(void)endOfPlay:(NSNotification *)sender
    {
       NSLog(@"播放完成,下一首");
       [self nextMusic];
    }
    
    -(void)nextMusic{
       //下一首
       [self pauseMusic];
       self.playingIndex ++ ;
       if(self.playingIndex >= self.musicInfos.count){
           self.playingIndex = 0;
       }
       [self playMusic];
       
       if(self.playerChangeBlock){
           self.playerChangeBlock(2);
       }
    }
    
    -(void)upMusic{
       //上一首
       [self pauseMusic];
       self.playingIndex -- ;
       if(self.playingIndex < 0){
           self.playingIndex = self.musicInfos.count-1;
       }
       [self playMusic];
       
       if(self.playerChangeBlock){
           self.playerChangeBlock(-2);
       }
    }
    
    // 观察者的处理方法, 观察的是Item的status状态.
    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    {
       if ([keyPath isEqualToString:@"status"]) {
           switch ([[change valueForKey:@"new"] integerValue]) {
               case AVPlayerItemStatusUnknown:
                   NSLog(@"不知道什么错误");
                   //回调下一首
                   break;
               case AVPlayerItemStatusReadyToPlay:
                   // 只有观察到status变为这种状态,才会真正的播放.
                   [self continuePlayMusic];
                   break;
               case AVPlayerItemStatusFailed:
                   // mini设备不插耳机或者某些耳机会导致准备失败.
                   NSLog(@"准备失败");
                   break;
               default:
                   break;
           }
       }
    }
    
    //停止//会直接销毁
    -(void)stopMusic{
       if(self.musicPlayer){
           [self pauseMusic];
           [self.musicPlayer.currentItem removeObserver:self forKeyPath:@"status"];//移除监听
           self.musicPlayer  =  nil;
       }
    }
    
    //继续播放
    -(void)continuePlayMusic
    {
       if(self.musicPlayer){
           if(self.musicPlayer.currentItem){
               [self loadLockViewInfo];
               [self.musicPlayer play];
               self.isplaying = YES;
           }
       }
    }
    //暂停//后面可以继续播放
    -(void)pauseMusic{
       if(self.musicPlayer){
           [self.musicPlayer pause];
           self.isplaying = NO;
       }
    }
    
    //更改当前音乐的播放进度,从多少秒开始播放(time 秒)
    -(void)playMusicWithSlider:(NSInteger)time{
       if(self.musicPlayer){
            // 先暂停
              [self pauseMusic];
              // 跳转
              [self.musicPlayer seekToTime:CMTimeMake(time * self.musicPlayer.currentTime.value, 1) completionHandler:^(BOOL finished) {
                  if (finished == YES) {
                      [self continuePlayMusic];
                  }
              }];
       }
    }
    
    -(BOOL)isPlaying{
       if(self.musicPlayer){
           return self.isplaying;
       }
       return NO;
    }
    
    //获取播放进度;秒(当前歌曲已经播放了多少秒)
    -(NSInteger)playingSlider{
       if (self.musicPlayer.currentItem) {
           return CMTimeGetSeconds(self.musicPlayer.currentTime);
       }
       return 0;
    }
    //获取歌曲时长;秒(当前歌曲已经播放了多少秒)
    -(NSInteger)musicDuration{
       if (self.musicPlayer.currentItem) {
           return CMTimeGetSeconds(self.musicPlayer.currentItem.duration);
       }
       return 0;
    }
    
    -(void)playMusics:(NSArray<MusicPlayerInfo*> *)musicInfos endTime:(NSInteger)alltime stateChange:(void (^)(int ischange))completion{
       self.playerChangeBlock = completion;
       self.musicInfos = musicInfos;
       self.endPlayTime = alltime;
       if(!self.changeTimer){
           self.changeTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(changeplaytime) userInfo:nil repeats:YES];//开始计时
       }
       [self stopMusic];
       self.playingIndex = 0;
       [self playMusic];
    }
    
    -(MusicPlayerInfo *)playingMusicInfo{
       MusicPlayerInfo * musicInfo = self.musicInfos[self.playingIndex];
       MusicPlayerInfo * result = [[MusicPlayerInfo alloc]init];
       result.filePath = musicInfo.filePath;
       result.name = musicInfo.name;
       result.picPath = musicInfo.picPath;
       result.playerName = musicInfo.playerName;
       result.needBack = musicInfo.needBack;
       result.slider = [self playingSlider];
       result.isplay = [self isPlaying];
       return musicInfo;
    }
    
    -(void)setIsplaying:(BOOL)isplaying{
       _isplaying = isplaying;
       //通知播放状态改变
       if(self.playerChangeBlock){
           self.playerChangeBlock(isplaying?1:-1);
       }
    }
    
    
    -(void)changeplaytime{
       self.endPlayTime --;
       [self loadLockViewInfo];
       if(self.endPlayTime == 0){
           [self pauseMusic];//暂停
       }
    }
    
    +(void)applicationWillEnterForeground{
       if(_instance && !_instance.isBackPlayer){//不是后台播放,切换后台需暂停
           [_instance pauseMusic];
       }
    }
    +(void)applicationDidBecomeActive{
       if(_instance && !_instance.isBackPlayer){//不是后台播放
           [_instance continuePlayMusic];
       }
    }
    @end
    @implementation MusicPlayerInfo
    
    @end

    点个赞再走呗。。。

    如有疑问,联系作者 

    博客园:这个我不知道诶

  • 相关阅读:
    dubbo学习(一)认识
    MySQL学习(九)小结
    MySQL学习(八)删除表数据
    MySQL学习(六)change-buffer
    RPC 学习(一)认识
    MySQL学习(五)事务隔离
    MySQL学习(四)死锁及死锁检测
    计算机操作系统 --- 进程和进程的上下文切换
    MySQL 学习(三)事务学习
    消息队列(七)--- RocketMQ延时发送和消息重试(半原创)
  • 原文地址:https://www.cnblogs.com/Yongersblog/p/12911567.html
Copyright © 2020-2023  润新知