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 <MediaPlayer/MPMediaItem.h>. 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(播放信息中心)為應用程序提供設置當前正在播放的信息的接口; 此信息將顯示在正在播放信息類型調用的任何位置,例如鎖屏下或者應用程序切換中; 正在播放的信息字典包含一組正在播放項的元數據屬性,這些屬性常量列表在<MediaPlayer/MPMediaItem.h>有提供; 這些屬性目前提供的包括: 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<NSString *, id> *nowPlayingInfo;
也就是說,可以用defaultCenter來獲取當前的MPNowPlayingInfoCenter,然后在 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 <AVFoundation/AVFoundation.h> #import <MediaPlayer/MediaPlayer.h> @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