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
点个赞再走呗。。。
如有疑问,联系作者
博客园:这个我不知道诶