关于监控苹果耳机,目的上主要分为监控耳机插入,拔出,和耳机线控,因为最近做了关于苹果耳机线控方面的需求,所以想针对苹果耳机线控在这唠嗑一下。
苹果耳机3个按键(音量加,音量减,中间功能键)的经典设计已经延续了十几年,功能上我们谈到的耳机线控是监听耳机中间功能键的动作,作出功能上的响应。
如何支持耳机线控
1.引入AVFoundation框架
具体引入方式不截图,不细说了,只有引入了AVFoundation
框架,耳机线控才能生效。
2.允许传递远程控制事件
允许传递远程控制事件
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
结束传递远程控制事件
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
因为在允许接受远程控制事件时,UI控件必须为第一响应者,所以需要在beginReceivingRemoteControlEvents
方法后面增加一个becomeFirstResponder
方法。
记得补上
-(BOOL)canBecomeFirstResponder{
return YES;
}
3.启用远程事件接收
启动远程事件接受的应用才能支持耳机线控,当然也可以监控其他外部控件的响应,比如自拍杆拍照之类的。
启用远程事件接收的核心就是UIResponder
类中的- (void)remoteControlReceivedWithEvent:(UIEvent *)event
方法。
//received remote event
-(void)remoteControlReceivedWithEvent:(UIEvent *)event{
NSLog(@"event tyipe:::%ld subtype:::%ld",(long)event.type,(long)event.subtype);
//type==2 subtype==单击暂停键:103,双击暂停键104
if (event.type == UIEventTypeRemoteControl) {
switch (event.subtype) {
case UIEventSubtypeRemoteControlPlay:{
NSLog(@"play---------");
}break;
case UIEventSubtypeRemoteControlPause:{
NSLog(@"Pause---------");
}break;
case UIEventSubtypeRemoteControlStop:{
NSLog(@"Stop---------");
}break;
case UIEventSubtypeRemoteControlTogglePlayPause:{
//单击暂停键:103
NSLog(@"单击暂停键:103");
}break;
case UIEventSubtypeRemoteControlNextTrack:{
//双击暂停键:104
NSLog(@"双击暂停键:104");
}break;
case UIEventSubtypeRemoteControlPreviousTrack:{
NSLog(@"三击暂停键:105");
}break;
case UIEventSubtypeRemoteControlBeginSeekingForward:{
NSLog(@"单击,再按下不放:108");
}break;
case UIEventSubtypeRemoteControlEndSeekingForward:{
NSLog(@"单击,再按下不放,松开时:109");
}break;
default:
break;
}
}
}
为了保证应用一定能够接受到远程控制请求,包括耳机线控,
beginReceivingRemoteControlEvents
方法和remoteControlReceivedWithEvent
方法建议写在应用的启动的入口,AppDelegate
或者RootViewController
中。
写到这里,理论上我们就可以疯狂的点击耳机功能键,监控耳机响应各种华丽的操作了,但在一顿猛烈的点击之后,日志板上一片风平浪静,我的预想并没有达到。。。
实现耳机线控的三个前提
整理一下思绪,实现耳机线控的按键的监控是有以下三个前提
1.启用远程事件接收(使用[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];方法)。
2.对于UI控件同样要求必须是第一响应者(对于视图控制器UIViewController或者应用程序UIApplication对象监听无此要求)。
3.应用程序必须是当前音频的控制者,也就是在系统控制中心中当前音频播放程序必须是我们自己开发程序。
第一第二点比较好理解,第三点是何解?师爷。翻译翻译。
下拉我的手机,控制中心中有一个音乐快捷播放器。
如图所示,我的控制中心的音乐播放器被 米津玄师的Lemon 霸占着。。
第三点是指只有应用霸占着控制中心的音乐播放器时,才可以实现耳机的线控。此时我的应用明显没有具备这个条件。
如何获取播放器的控制权
既然已经知道了问题的原因,那我们就需要对症下药了。
为了获取控制中心播放器的控制权,我决定在应用开启的时候使用AVAudioPlayer
播放一段几秒中的空白音频,此处是我随便写的一个demo,具体播放空白音频的时机各位开发者可以视情况而定,比如如果是用耳机线控做相机功能的朋友,可以选择在打开相机的时候使用AVAudioPlayer
播放空白音频,获取播放器权限。
AVAudioPlayer
的使用我就不细说了,直接上代码。
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"20" ofType:@"mp3"];
NSURL *url = [NSURL fileURLWithPath:filePath];
self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url fileTypeHint:nil error:nil];
if ([self.player prepareToPlay]) {
NSLog(@"音频文件准备就绪");
}
[self.player play];
Class playingInfoCenter = NSClassFromString(@"MPNowPlayingInfoCenter");
if (playingInfoCenter) {
NSMutableDictionary *songInfo = [NSMutableDictionary dictionary];
//歌曲名称
[songInfo setObject:@"呜啦呜啦" forKey:MPMediaItemPropertyTitle];
//演唱者
[songInfo setObject:@"JOJO" forKey:MPMediaItemPropertyArtist];
[songInfo setObject:[[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:@"MP.png"]] forKey:MPMediaItemPropertyArtwork];
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];
}
为了证明确实获取了控制中心音乐播放器权限,使用MPNowPlayingInfoCenter
设置播放器信息。
成功获取播放器权限后,我再次对我的耳机一整狂点,remoteControlReceivedWithEvent
方法执行成功,nice~
总结
本次做耳机线控踩的坑主要是第三点前提
应用程序必须是当前音频的控制者,也就是在系统控制中心中当前音频播放程序必须是我们自己开发程序。
这点不太理解,花费了一天多的时间求索。所幸最后成功了,但解决问题的方法个人感觉并不是很聪明,如果有更好办法的朋友欢迎留言指教。
参考博客:
聊聊iOS开发中耳机的那点事(监听耳机拔插、耳机线控)
关于MPNowPlayingInfoCenter和MPRemoteCommandCenter