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

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

 

前言


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:https://yun.baidu.com/share/link?shareid=2650043582&uk=4079807053

文/神经骚栋(简书作者)
原文链接:http://www.jianshu.com/p/87f3f2024038

posted on 2016-09-03 22:49  &#127774;Bob  阅读(3757)  评论(0编辑  收藏  举报

导航