QQ音乐播放的过程中,锁屏状态下的效果如下:
也就是说,QQ音乐播放过程中,添加锁屏远程事件的监听。
本文只记录本人知道的小知识点,不提供完整的代码。
实现的原理:
(1)获取锁屏歌曲信息中心:MPNowPlayingInfoCenter
(2)设置锁屏下要显示的歌曲的信息
(3)启动远程事件的监听
1.MPNowPlayingInfoCenter简要说明:
(1)官方文档对MPNowPlayingInfoCenter的解说如下:
// -----------------------------------------------------------------------------
// MPNowPlayingInfoCenter provides an interface for setting the current now
// playing information for the application. This information will be displayed
// wherever now playing information typically appears, such as the lock screen
// and app switcher. The now playing info dictionary contains a group of
// metadata properties for a now playing item. The list of property constants
// is available in . The properties which are
// currently supported include:
//
// MPMediaItemPropertyAlbumTitle
// MPMediaItemPropertyAlbumTrackCount
// MPMediaItemPropertyAlbumTrackNumber
// MPMediaItemPropertyArtist
// MPMediaItemPropertyArtwork
// MPMediaItemPropertyComposer
// MPMediaItemPropertyDiscCount
// MPMediaItemPropertyDiscNumber
// MPMediaItemPropertyGenre
// MPMediaItemPropertyPersistentID
// MPMediaItemPropertyPlaybackDuration
// MPMediaItemPropertyTitle
//
// In addition, metadata properties specific to the current playback session
// may also be specified -- see "Additional metadata properties" below.
上面那段话大体的意思如下:
MPNowPlayingInfoCenter(播放信息中心)为应用程序提供设置当前正在播放的信息的接口; 此信息将显示在正在播放信息类型调用的任何位置,例如锁屏下或者应用程序切换中; 正在播放的信息字典包含一组正在播放项的元数据属性,这些属性常量列表在
此外,音乐播放必须支持后台播放的功能。 另外,当前播放信息中心还提供了一个方法和一个属性如下:有提供; 这些属性目前提供的包括: MPMediaItemPropertyAlbumTitle (标题) MPMediaItemPropertyAlbumTrackCount(专辑歌曲数) MPMediaItemPropertyAlbumTrackNumber (专辑歌曲编号) MPMediaItemPropertyArtist (艺术家/歌手) MPMediaItemPropertyArtwork (封面图片 MPMediaItemArtwork 类型) MPMediaItemPropertyComposer (作曲) MPMediaItemPropertyDiscCount (专辑数) MPMediaItemPropertyDiscNumber (专辑编号) MPMediaItemPropertyGenre (类型\流派) MPMediaItemPropertyPersistentID (唯一标识符) MPMediaItemPropertyPlaybackDuration (歌曲时长) MPMediaItemPropertyTitle (歌曲名称) 此外,音乐播放必须支持后台播放的功能。 /// Returns the default now playing info center. /// The default center holds now playing info about the current application. + (MPNowPlayingInfoCenter *)defaultCenter;/// The current now playing info for the center. /// Setting the info to nil will clear it. @property (nonatomic, copy, nullable) NSDictionary
也就是说,可以用defaultCenter来获取当前的MPNowPlayingInfoCenter,然后在 nowPlayingInfo 以字典的形式设置 锁屏中的歌曲信息。*nowPlayingInfo;
其中,这个类,还提供了一些额外的元组属性,如下:
MP_EXTERN NSString *const MPNowPlayingInfoPropertyElapsedPlaybackTime 当前时间 NSNumber
MP_EXTERN NSString *const MPNowPlayingInfoPropertyPlaybackRate
MP_EXTERN NSString *const MPNowPlayingInfoPropertyDefaultPlaybackRate
MP_EXTERN NSString *const MPNowPlayingInfoPropertyPlaybackQueueIndex
MP_EXTERN NSString *const MPNowPlayingInfoPropertyPlaybackQueueCount
MP_EXTERN NSString *const MPNowPlayingInfoPropertyChapterNumber
MP_EXTERN NSString *const MPNowPlayingInfoPropertyChapterCount
MP_EXTERN NSString *const MPNowPlayingInfoPropertyAvailableLanguageOptions MPNowPlayingInfoLanguageOptionGroup
MP_EXTERN NSString *const MPNowPlayingInfoPropertyCurrentLanguageOptions
找到了一个对这些属性解说不错的文档:
蹩脚英文翻译系列:(未标注版本的键均为iOS8及以下可用)
Name | Type | meaning |
---|---|---|
MPMediaItemPropertyAlbumTitle | NSString | 专辑歌曲数 |
MPMediaItemPropertyAlbumTrackCount | NSNumber of NSUInteger | 专辑歌曲数 |
MPMediaItemPropertyAlbumTrackNumber | NSNumber of NSUInteger | 艺术家/歌手 |
MPMediaItemPropertyArtist | NSString | 艺术家/歌手 |
MPMediaItemPropertyArtwork | MPMediaItemArtwork | 封面图片 MPMediaItemArtwork类型 |
MPMediaItemPropertyComposer | NSString | 作曲 |
MPMediaItemPropertyDiscCount | NSNumber of NSUInteger | 专辑数 |
MPMediaItemPropertyDiscNumber NSNumber of NSUInteger | 专辑编号 | |
MPMediaItemPropertyGenre | NSString | 类型/流派 |
MPMediaItemPropertyPersistentID | NSNumber of uint64_t | 唯一标识符 |
MPMediaItemPropertyPlaybackDuration | NSNumber of NSTimeInterval | 歌曲时长 NSNumber类型 |
MPMediaItemPropertyTitle | NSString | 歌曲名称 |
MPNowPlayingInfoPropertyElapsedPlaybackTime | NSNumber (double) | 在播资源的时间流逝,s为单位。流逝时间会从播放时间和播放速率中自动计算,不合适频繁得更新 |
MPNowPlayingInfoPropertyPlaybackRate | NSNumber (double) | 在播资源的速率(保持与APP内播放器的速率一致) |
MPNowPlayingInfoPropertyDefaultPlaybackRate | NSNumber (double) | 在播资源的“默认”播放速率,当你的APP需要播放资源的播放速率默认都是大于1的,那么就应该使用这属性 |
MPNowPlayingInfoPropertyPlaybackQueueIndex | NSNumber (NSUInteger) | 应用重放队列中,当前播放项的索引。注意索引值从0开始 |
MPNowPlayingInfoPropertyPlaybackQueueCount | NSNumber (NSUInteger) | 应用重放队列的总资源数目 |
MPNowPlayingInfoPropertyChapterNumber | NSNumber (NSUInteger) | 这在播放的部分,索引值从0开始 |
MPNowPlayingInfoPropertyChapterCount | NSNumber (NSUInteger) | 在播资源的总章节数目 |
MPNowPlayingInfoPropertyIsLiveStream(iOS 10.0) | NSNumber (BOOL) | 表示当前的资源是不是实时流 |
MPNowPlayingInfoPropertyAvailableLanguageOptions(iOS 9.0) | NSArrayRef of MPNowPlayingInfoLanguageOptionGroup | 在播资源的一组可用的语言类型。在给定组中一次只能播放一种语言类型的资源 |
MPNowPlayingInfoPropertyCurrentLanguageOptions(iOS 9.0) | NSArray of MPNowPlayingInfoLanguageOption | 当前播放项目的语言选项列表 |
MPNowPlayingInfoCollectionIdentifier(iOS 9.3) | NSString | 表示当前播放资源所归属的那个集合的标识符,可指作者、专辑、播放列表等。可用于请求重新播放这个集合。 |
MPNowPlayingInfoPropertyExternalContentIdentifier(iOS 10.0) | NSString | 一个不暴露的唯一标志符,标志当前正在播放的item,贯穿APP重启。可使用任何格式,仅用于引用这个item和返回到正在播放资源的APP中 |
MPNowPlayingInfoPropertyExternalUserProfileIdentifier(iOS 10.0) | NSString | 一个可选型的不暴露的标志,标志当前正在播放的资源的配置文件,贯穿APP重启。可使用任何格式,仅用于返回到这个配置文件对应的正在播放视频的APP |
MPNowPlayingInfoPropertyServiceIdentifier(iOS 11.0) | NSString | 服务商的唯一标志。如果当前播放的资源属于一个频道或者是定于的服务类型,这个ID可以用于区分和协调特定服务商的多种资源类型 |
MPNowPlayingInfoPropertyPlaybackProgress(iOS 10.0) | NSNumber (float) | 表示当前播放资源的播放进度,0.0表示未开始,1.0表示完全浏览完。区分于ElapsedPlaybackTime,无需更高的精度要求。如:当字幕开始滚动时,这个电影可能被用户期望开始播放(由字幕驱动播放进度) |
MPNowPlayingInfoPropertyMediaType | NSNumber (MPNowPlayingInfoMediaType) | 指定当前媒体类型,用于确定系统显示的用户界面类型 |
MPNowPlayingInfoPropertyAssetURL(iOS 10.3) | NSURL | 指向当前正播放的视频或音频资源的URL。可将视频缩略图或者音频的波普图使用于系统的UI上 |
2.锁屏下要显示歌曲信息的设置
从上文中,已经可以知道,用
MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter];
来获取锁屏信息控制中心,然后把锁屏情况下要显示的图片、歌曲名字、歌手、歌词、时间等信息以字典的形式赋值给center的nowPlayInfo属性。
示例代码如下:
MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter];
NSMutableDictionary *infoDic = [NSMutableDictionary dictionary];
[infoDic setObject:@"泡沫" forKey:MPMediaItemPropertyAlbumTitle];
[infoDic setObject:@"歌手" forKey:MPMediaItemPropertyArtist];
[infoDic setObject:[[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:@"61149b0491243c749fc871e67550a7f6"]] forKey:MPMediaItemPropertyArtwork];
[infoDic setObject:@"200" forKey:MPMediaItemPropertyPlaybackDuration];
[infoDic setObject:@"歌词" forKey:MPMediaItemPropertyTitle];
center.nowPlayingInfo = infoDic;
3.锁屏远程事件的监听的简要说明:
(1)ios71.版本
可以用的是[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
然后监听remoteControlReceivedWithEvent:这个方法,
event的类中有UIEventSubtype的subtype。
subtype的类型如下:
typedef NS_ENUM(NSInteger, UIEventSubtype) {
// available in iPhone OS 3.0
UIEventSubtypeNone = 0,
// for UIEventTypeMotion, available in iPhone OS 3.0
UIEventSubtypeMotionShake = 1,
// for UIEventTypeRemoteControl, available in iOS 4.0
UIEventSubtypeRemoteControlPlay = 100, //播放
UIEventSubtypeRemoteControlPause = 101, //暂停
UIEventSubtypeRemoteControlStop = 102, //停止
UIEventSubtypeRemoteControlTogglePlayPause = 103, //耳机上的播放暂停命令
UIEventSubtypeRemoteControlNextTrack = 104, //下一首
UIEventSubtypeRemoteControlPreviousTrack = 105, //上一首
UIEventSubtypeRemoteControlBeginSeekingBackward = 106, //开始后退
UIEventSubtypeRemoteControlEndSeekingBackward = 107, //后退结束
UIEventSubtypeRemoteControlBeginSeekingForward = 108, //开始快进
UIEventSubtypeRemoteControlEndSeekingForward = 109, //快进结束
};
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
NSLog(@"---event.type = %ld", (long)event.subtype);
//在这里面监听操作的类型
}
(2)iOS7.1以后
可以使用MPRemoteCommandCenter这个类的方法,这个类提供了一个类方法:sharedCommandCenter;
提供一个不错可参考的链接:https://www.jianshu.com/p/b9cc97db16b8
MPRemoteCommandCenter是获取到这个单例对象后,使用共享的这个MPRemoteCommand对象,用于响应各种远程控制事件配置自己的需求。
如:像网易云音乐一样,在锁屏以及多媒体系统UI界面配置滑动播放进度(seekTime),下一曲,上一曲,喜欢,不喜欢等配置;
MPRemoteCommandCenter提供的配置信息如下:
// Playback Commands
@property (nonatomic, readonly) MPRemoteCommand *pauseCommand; //暂停
@property (nonatomic, readonly) MPRemoteCommand *playCommand; //播放
@property (nonatomic, readonly) MPRemoteCommand *stopCommand; //停止
@property (nonatomic, readonly) MPRemoteCommand *togglePlayPauseCommand; //耳机线控制暂停和播放
@property (nonatomic, readonly) MPRemoteCommand *enableLanguageOptionCommand MP_API(ios(9.0), macos(10.12.2)); //不知
@property (nonatomic, readonly) MPRemoteCommand *disableLanguageOptionCommand MP_API(ios(9.0), macos(10.12.2)); //不知
@property (nonatomic, readonly) MPChangePlaybackRateCommand *changePlaybackRateCommand; //不知
@property (nonatomic, readonly) MPChangeRepeatModeCommand *changeRepeatModeCommand; //不知
@property (nonatomic, readonly) MPChangeShuffleModeCommand *changeShuffleModeCommand; 不知
// Previous/Next Track Commands
@property (nonatomic, readonly) MPRemoteCommand *nextTrackCommand; //下一首
@property (nonatomic, readonly) MPRemoteCommand *previousTrackCommand; //上一首
// Skip Interval Commands
@property (nonatomic, readonly) MPSkipIntervalCommand *skipForwardCommand; //快进几秒(如果与下一首同时设置,优先显示快进)
@property (nonatomic, readonly) MPSkipIntervalCommand *skipBackwardCommand; //快退几秒(如果与上一首同时设置,优先显示快退)
// Seek Commands
@property (nonatomic, readonly) MPRemoteCommand *seekForwardCommand; //不知
@property (nonatomic, readonly) MPRemoteCommand *seekBackwardCommand; //不知
@property (nonatomic, readonly) MPChangePlaybackPositionCommand *changePlaybackPositionCommand MP_API(ios(9.1), macos(10.12.2));
// Rating Command
@property (nonatomic, readonly) MPRatingCommand *ratingCommand; //设置倍速,不知道在哪里显示
// Feedback Commands
// These are generalized to three distinct actions. Your application can provide
// additional context about these actions with the localizedTitle property in
// MPFeedbackCommand.
@property (nonatomic, readonly) MPFeedbackCommand *likeCommand; //设置喜欢
@property (nonatomic, readonly) MPFeedbackCommand *dislikeCommand; //设置不喜欢
@property (nonatomic, readonly) MPFeedbackCommand *bookmarkCommand; //添加标签
设置的方式如下:
MPRemoteCommandCenter *rcc = [MPRemoteCommandCenter sharedCommandCenter];
//添加暂停监听
[rcc.pauseCommand addTarget:self action:@selector(playOrPauseEvent:)];
整体的代码如下:
//
// ViewController.m
// 音效播放
//
// Created by 珠珠 on 2019/10/31.
// Copyright © 2019 珠珠. All rights reserved.
//
#import "ViewController.h"
#import
#import
@interface ViewController ()
@property (nonatomic,strong) AVAudioPlayer *player ;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//设置音乐的后台播放,注意background mode中需要勾选上
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
//获取信息中心
MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter];
//设置要锁屏要显示的基本信息
NSMutableDictionary *infoDic = [NSMutableDictionary dictionary];
[infoDic setObject:@"泡沫" forKey:MPMediaItemPropertyAlbumTitle];
[infoDic setObject:@"歌手" forKey:MPMediaItemPropertyArtist];
[infoDic setObject:[[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:@"61149b0491243c749fc871e67550a7f6"]] forKey:MPMediaItemPropertyArtwork];
[infoDic setObject:@"200" forKey:MPMediaItemPropertyPlaybackDuration];
[infoDic setObject:@"歌词" forKey:MPMediaItemPropertyTitle];
//给信息中心赋值
center.nowPlayingInfo = infoDic;
//添加远程事件监听
NSString *version= [UIDevice currentDevice].systemVersion;
if(version.doubleValue <=7.1) {
//iOS版本7.1以下的建议使用这个方法
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}else{
//iOS版本7.1以上的的建议使用这个方法
[self addRemoteCommandCenter];
}
}
#pragma mark - 基本播放操作
//开始播放
- (IBAction)player:(id)sender {
[self.player play];
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
[self.view addSubview:view];
}
//暂停播放
- (IBAction)pause:(id)sender {
[self.player pause];
}
//停止播放
- (IBAction)stop:(id)sender {
[self.player stop];
}
//前进5秒
- (IBAction)qianJin5s:(id)sender {
self.player.currentTime += 5;
}
//后退5秒
- (IBAction)houTui5s:(id)sender {
self.player.currentTime -= 5;
}
//2倍速度播放
- (IBAction)faster:(id)sender {
self.player.rate = 2;
}
//播放一次
- (IBAction)playOnce:(id)sender {
self.player.numberOfLoops = 0;
}
//播放3次
- (IBAction)playThirst:(id)sender {
self.player.numberOfLoops = 2;
}
//循环播放
- (IBAction)playAllTheTime:(id)sender {
self.player.numberOfLoops = -1;
}
//听筒播放
- (IBAction)tingTongPlay:(id)sender {
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:0 error:nil];
}
//扬声器播放
- (IBAction)outSpeakerPlayer:(id)sender {
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:nil];
}
#pragma mark - MPRemoteCommandCenter相关的方法
- (void)addRemoteCommandCenter {
MPRemoteCommandCenter *rcc = [MPRemoteCommandCenter sharedCommandCenter];
//添加暂停监听
[rcc.pauseCommand addTarget:self action:@selector(playOrPauseEvent:)];
//添加播放监听
[rcc.playCommand addTarget:self action:@selector(playOrPauseEvent:)];
//下一首
[rcc.nextTrackCommand addTarget:self action:@selector(nextCommandEvent:)];
//上一首
[rcc.previousTrackCommand addTarget:self action:@selector(previousTrackCommand:)];
//耳机暂停和播放的监听
[rcc.togglePlayPauseCommand addTarget:self action:@selector(togglePlayPauseCommand:)];
//快进(如果同时设置了下一首和快进,那么锁屏下只会显示快进的按钮)
[rcc.skipForwardCommand addTarget:self action:@selector(handleSkipForward:)];
[rcc.skipForwardCommand setPreferredIntervals:@[@(20)]]; // 设置快进时间(最大 99)
//快退(如果同时设置了下一首和后退,那么锁屏下只会显示快退的按钮)
[rcc.skipBackwardCommand addTarget:self action:@selector(handleSkipBack:)];
[rcc.skipBackwardCommand setPreferredIntervals:@[@20]]; // 设置快退时间(最大99)
// [self feedbackCommand]
}
- (void)playOrPauseEvent:(MPRemoteCommand *)command
{
NSLog(@"播放或者暂停");
if (self.player.isPlaying) {
[self.player pause];
}else {
[self.player play];
}
}
- (void)nextCommandEvent:(MPRemoteCommand *)command
{
NSLog(@"%@",@"下一曲");
}
- (void)previousTrackCommand:(MPRemoteCommand *)command
{
NSLog(@"%@",@"上一曲");
}
- (void)togglePlayPauseCommand:(MPRemoteCommand *)command {
NSLog(@"耳机的开始和暂停");
if (self.player.isPlaying) {
[self.player pause];
}else {
[self.player play];
}
}
- (void)handleSkipForward:(MPRemoteCommand *)command
{
NSLog(@"快进%@",command);
}
- (void)handleSkipBack:(MPRemoteCommand *)command
{
NSLog(@" 快退%@",command);
}
-(void)feedbackCommand:(MPRemoteCommandCenter *)rcc
{
MPFeedbackCommand *likeCommand = [rcc likeCommand];
[likeCommand setEnabled:YES];
[likeCommand setLocalizedTitle:@"I love it"]; // can leave this out for default
[likeCommand addTarget:self action:@selector(likeEvent:)];
MPFeedbackCommand *dislikeCommand = [rcc dislikeCommand];
[dislikeCommand setEnabled:YES];
[dislikeCommand setActive:YES];
[dislikeCommand setLocalizedTitle:@"I hate it"]; // can leave this out for default
[dislikeCommand addTarget:self action:@selector(dislikeEvent:)];
BOOL userPreviouslyIndicatedThatTheyDislikedThisItemAndIStoredThat = YES;
if (userPreviouslyIndicatedThatTheyDislikedThisItemAndIStoredThat) {
[dislikeCommand setActive:YES];
}
MPFeedbackCommand *bookmarkCommand = [rcc bookmarkCommand];
[bookmarkCommand setEnabled:YES];
[bookmarkCommand addTarget:self action:@selector(bookmarkEvent:)];
}
-(void)dislikeEvent: (MPFeedbackCommandEvent *)feedbackEvent
{
NSLog(@"Mark the item disliked");
}
-(void)likeEvent: (MPFeedbackCommandEvent *)feedbackEvent
{
NSLog(@"Mark the item liked");
}
-(void)bookmarkEvent: (MPFeedbackCommandEvent *)feedbackEvent
{
NSLog(@"Bookmark the item or playback position");
}
#pragma mark - 远程事件的监听:[[UIApplication sharedApplication] beginReceivingRemoteControlEvents]
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
NSLog(@"---event.type = %ld", (long)event.subtype);
}
#pragma mark - 懒加载播放器
- (AVAudioPlayer *)player {
if (!_player) {
//获取播放的路径 paomo.mp3 2018-11-27 10_36_51 1.wav
NSURL *path = [[NSBundle mainBundle] URLForResource:@"paomo.mp3" withExtension:nil];
//根据路径创建播放对象
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:path error:nil];
//如果这个属性不设置的话,那么不能设置倍速播放的功能
player.enableRate = YES;
//准备播放
[player prepareToPlay];
_player = player;
}
return _player;
}
@end
另参考链接:http://www.cocoachina.com/articles/15767