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

 

 

點個贊再走唄。。。

 

如有疑問,聯系作者 

博客園:這個我不知道誒


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM