美文网首页iOS软件开发键盘上的鼓手iOS开发资料收集
聊聊iOS开发中耳机的那点事(监听耳机拔插、耳机线控)

聊聊iOS开发中耳机的那点事(监听耳机拔插、耳机线控)

作者: 神经骚栋 | 来源:发表于2016-07-27 23:38 被阅读4145次

如果说一个项目出现的最重大的事故,那无疑就是开发人员使用了不可控的元素.

前言


iOS开发当中有关于视音频播放的开发不在少数,用户时常会使用到一种输出设备,那就是"耳机",这一篇博客写的就是关于耳机的一些开发相关的技术点.

检测耳机是否插入


看到上面的标题的时候一定要注意,这里说的是"检测耳机是否插入",这里只是一次性的检测,不是实时监控耳机的拔插,但是有一些时候,下面的这个方法已经足够满足我们的开发需求了.

首先,我们需要导入AVFoundation.framework这个框架如下图.

导入AVFoundation.framework框架

然后导入头文件,实现下面的方法

#import <AVFoundation/AVFoundation.h>  //导入头文件
  
- (BOOL)isHeadsetPluggedIn {  
    AVAudioSessionRouteDescription* route = [[AVAudioSession sharedInstance] currentRoute];  
    for (AVAudioSessionPortDescription* desc in [route outputs]) {  
        if ([[desc portType] isEqualToString:AVAudioSessionPortHeadphones])  
            return YES;  
    }  
    return NO;  
}  

监听耳机的拔插 (iOS6.0的实现)


其实这几天一直在看网上的相关的资料,发现监听耳机拔插大多数是一些iOS6.0左右的老方法,我们就先抛砖引玉一下,先看看iOS6.0是如何监听耳机拔插事件.

首先,我们需要导入AVFoundation.framework这个框架如下图.

导入AVFoundation.framework框架
iOS6.0的监听耳机拔插主要是对AVAudioSession单例对象中的AudioSessionAddPropertyListener 这个 block函数的实现完成的,这个函数在耳机拔插的时候调用才会调用.这里的监听耳机拔插事件我直接写在了AppDelegate中- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions,我们看一下具体的代码实现.

#import "AppDelegate.h"
#import <AVFoundation/AVFoundation.h>//导入头文件

@interface AppDelegate ()<AVAudioSessionDelegate>

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    [[AVAudioSession sharedInstance] setDelegate:self];//初始化单例设置代理
    
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];//设置AVAudioSession单例对象的类型

    AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange,audioRouteChangeListenerCallback, (__bridge void *)(self));//调用block函数

    return YES;
}



//对block函数其中的方法进行实现
void audioRouteChangeListenerCallback (void *inUserData, AudioSessionPropertyID inPropertyID, UInt32 inPropertyValueSize,const void *inPropertyValue ) {
    
    // ensure that this callback was invoked for a route change
    
    if (inPropertyID != kAudioSessionProperty_AudioRouteChange) return;
    
    {
        
        // Determines the reason for the route change, to ensure that it is not
        
        //      because of a category change.
        
        CFDictionaryRef routeChangeDictionary = (CFDictionaryRef)inPropertyValue;

        CFNumberRef routeChangeReasonRef = (CFNumberRef)CFDictionaryGetValue (routeChangeDictionary, CFSTR (kAudioSession_AudioRouteChangeKey_Reason) );
        
        SInt32 routeChangeReason;
        
        CFNumberGetValue (routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason);
    
        if (routeChangeReason == kAudioSessionRouteChangeReason_OldDeviceUnavailable) {

            //Handle Headset Unplugged
  
            NSLog(@"没有耳机!");
   
        } else if (routeChangeReason == kAudioSessionRouteChangeReason_NewDeviceAvailable) {
            
            //Handle Headset plugged in
            
            NSLog(@"有耳机!");

            
        }

    }
    
}

监听耳机的拔插 (iOS6.0之后的实现)


其实,如果不是一个有警告情结的程序猿,上面的监听耳机拔插实现已经完全能满足工作需求,但是对于我这么一个有警告情结的程序猿,心中对上面的程序运行过程出现的警告十分的反感,在上面的AVAudioSession很多方法被弃用之后,我们需要使用通知来实现监听耳机的拔插.所以就出现了下面的方法.

下面的方法我为了方便,就直接下载Demo的ViewController中,和上面一样,我们需要导入AVFoundation.framework这个框架.然后我们在ViewController导入头文件.然后我们需要监听的通知名称如下.

AVAudioSessionRouteChangeNotification //需要监听的通知名称
具体的代码实现,如下.

#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [[AVAudioSession sharedInstance] setActive:YES error:nil];//创建单例对象并且使其设置为活跃状态.

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioRouteChangeListenerCallback:)   name:AVAudioSessionRouteChangeNotification object:nil];//设置通知

}


//通知方法的实现
- (void)audioRouteChangeListenerCallback:(NSNotification*)notification
{
    
    NSDictionary *interuptionDict = notification.userInfo;
    NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
    switch (routeChangeReason) {
        case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
            NSLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
            tipWithMessage(@"耳机插入");
            break;
        case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
            NSLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
            tipWithMessage(@"耳机拔出,停止播放操作");
            break;
        case AVAudioSessionRouteChangeReasonCategoryChange:
            // called at start - also when other audio wants to play
            tipWithMessage(@"AVAudioSessionRouteChangeReasonCategoryChange");
            break;
    }
}

//不管何时,只要有通知中心的出现,在dealloc的方法中都要移除所有观察者.
-(void)dealloc{

    [[NSNotificationCenter defaultCenter] removeObserver:self];


}

//自定提醒窗口
NS_INLINE void tipWithMessage(NSString *message){
    
    dispatch_async(dispatch_get_main_queue(), ^{
        
        UIAlertView *alerView = [[UIAlertView alloc] initWithTitle:@"提示" message:message delegate:nil cancelButtonTitle:nil otherButtonTitles:nil, nil];
        
        [alerView show];
        
        [alerView performSelector:@selector(dismissWithClickedButtonIndex:animated:) withObject:@[@0, @1] afterDelay:0.9];
        
    });
    
}

监听耳机的拔插的注意事项(必看部分)


  • 1.开发人员测试监听耳机的拔插的代码的时候,要使用真机测试.模拟器是没有耳机插孔的,除非你给电脑凿一个孔.😁
  • 2.不管是使用iOS6.0之前的方法还是之后的方法,有个先决条件,那就是AVAudioSession这个类的单例对象必须在一开始初始化,否则不管是block隐形函数还是通知方法都不能实现.
  • 3.这个注意事项是个老生常谈的问题了,那就是一旦出现了通知中心,那么在当前控制器的dealloc方法中一定要移除通知中心的所有观察者.

耳机线控的按键的监控


耳机的线控
众所周知,iPhone耳机都是有线控部分的,那么我们该如何实现呢?,其实,实现的核心就是UIResponder类中的- (void)remoteControlReceivedWithEvent:(UIEvent *)event方法,这个方法有以下的两个作用.我们需要用到的就是第一个作用.
  • 接收到一个远程控制事件。比如耳机控制。
  • 允许传递远程控制事件,必须调用UIApplication的beginReceivingRemoteControlEvents方法;关闭远程控制,调用endReceivingRemoteControlEvents。

和上面一样,我们依然是需要导入AVFoundation.framework这个框架.然后我们在ViewController导入头文件.

当然了,实现耳机线控的按键的监控是有以下三个前提的.

  • 1.启用远程事件接收(使用[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];方法)。
  • 2.对于UI控件同样要求必须是第一响应者(对于视图控制器UIViewController或者应用程序UIApplication对象监听无此要求)。
  • 3.应用程序必须是当前音频的控制者,也就是在iOS 7中通知栏中当前音频播放程序必须是我们自己开发程序。

具体代码实现,如下.


-(BOOL)canBecomeFirstResponder{

    return YES;
}


//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;
        }
    }
}


麻辣味的Demo,小的早已为客官你准备好了,不知道客官需要几份?
---> 麻辣味Demo(麻辣等级:🔥🔥🔥)

相关文章

  • 聊聊iOS开发中耳机的那点事(监听耳机拔插、耳机线控)

    如果说一个项目出现的最重大的事故,那无疑就是开发人员使用了不可控的元素. 前言 iOS开发当中有关于视音频播放的开...

  • iOS 耳机线控

    最近适配耳机线控,记录一下问题首先,耳机线控三要素:1、开启接受耳机线控~~~~[[UIApplication s...

  • iOS 耳机线控那些坑

    关于监控苹果耳机,目的上主要分为监控耳机插入,拔出,和耳机线控,因为最近做了关于苹果耳机线控方面的需求,所以想针对...

  • iOS 中监听来电,耳机拔插事件

    项目中有音频播放,当手机有来电和耳机拔插等行为时,需要做一些操作 来电监听,创建CTCallCenter 初始化c...

  • android开发过程碰到的问题

    检测耳机拔出广播延迟1s 【问题】耳机拔出时会延迟1-2s才收到广播通知。 【现有方案】有线耳机拔插变化监听系统广...

  • 耳机线控

    耳机的拔插 NSError*error; [[AVAudioSessionsharedInstance]setCa...

  • iOS 事件机制

    事件 iOS 将事件分为三类: Touch Motion Remote像耳机线控…… Touch 事件 Touch...

  • 视频播放Tips

    主要涉及到Tips: 获取设备音量 静音模式失效 监听音量改变 设置设备音量 监听静音按钮 监听耳机拔插 1.获取...

  • 事件处理

    事件处理 ios中的事件触摸事件(捏合,点击)加速计事件远程控制事件(耳机线控调整音量大小) 触摸事件 响应者对象...

  • Android耳机线控

    注册耳机事件监听器 其中关键语句是 当其他软件注册耳机监听事件后,系统就不会向该监听发送事件;解决方法:经测试,在...

网友评论

  • _铲屎官_:楼主能在给一下demo么,remoteControlReceivedWithEvent方法就是不执行··
    _铲屎官_:@神经骚栋 谢谢,不过通过一下午尝试,解决这个问题了,不过方法很傻,预加载了一个avplayer,之后就走这个remoteControlReceivedWithEvent方法了,原因玄学啊。
    神经骚栋:文章已经更新,可在文章最后进行下载,我不知道了是否还好用昂~因为是16年写的,老铁
  • 小小小蚍蜉:花话说Demo就不再了啊
  • 言子玉:Mark 耳机
  • Sumency:在程序一启动的时候怎么让应用程序成为当前音频的控制者?
  • c4ibD3:耳机插入 拔出的功能好使 ,但是那个按键监听那块怎么没有demo 啊 感觉你写的不是很详细啊
  • 我的大名叫小爱:关于线控感觉写的不是很清楚啊.
  • 兔矢志:楼主问个问题,这些方法都是基于手机没有插入耳机时候。而我想判断的是启动应用程序的时候有没有插入耳机。 当我手机先插入耳机再去启动应用程序。代码中判断耳机插入的方法并不会走,这样我就不知道有没有插入耳机。有些操作就没法进行。楼主有方法判断到手机已经插入耳机的状态吗
    3045e3688ec5:你这个问题解决了吗?
  • 汣远:我用earpod,耳机线控监听,没反应..0.0 都没打印输出...
    在appdelegate里写了[[UIApplication sharedApplication]beginReceivingRemoteControlEvents];
    然后在controller里调用remoteControlReceivedWithEvent:(UIEvent *)event 耳机按了音量 暂停等,都没任何反应... :sob:
    神经骚栋:@汣远 哈哈哈 解决了就好!!!!技术感觉又提升了呢! :smile:
    汣远:@神经骚栋 后面解决了,加了这两句 [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    [self becomeFirstResponder]; :grin: 是我疏忽了..sorry
    神经骚栋:@汣远 :flushed: 在Airpod中我还真没有测试过.....
  • 卟师:我能转载分享吗?我会标注上作者和出处的
    神经骚栋: @卟师 可以的,对你有用途就好,随意😬😬😬
  • Damon4Zhou:iOS7及以上,当把声音切换到扬声器输出时,插拔耳机是不会收到通知,楼主可有办法???
    Damon4Zhou:@神经骚栋 不是这个导致的,因为当我把声音切换到耳机输出时,插拔耳机是可以收到通知的
    神经骚栋:@Damon4Zhou 你可以看一下文章里面的监听注意事项的第二条…

本文标题:聊聊iOS开发中耳机的那点事(监听耳机拔插、耳机线控)

本文链接:https://www.haomeiwen.com/subject/lovojttx.html