iOS开发-airpods的音频event适配

对于airpods的适配,主要适配其单耳机拿下pause,以及恢复和双耳机取下等情景的适配。

单耳机拿下

对于这些事件,airpods单耳机拿下属于pause事件Event,我们使用MediaPlayer框架。

iOS 7.1 Before

iOS 7.1之前,系统提供了

#import <MediaPlayer/MediaPlayer.h>

//开始接收远程控制事件 - UIApplication实例方法
- (void)beginReceivingRemoteControlEvents;
//结束接收远程控制事件
- (void)endReceivingRemoteControlEvents;
//远程控制事件的捕获处理 
- (void)remoteControlReceivedWithEvent:(UIEvent *)event

代码实现:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 在App启动后开启远程控制事件, 接收来自锁屏界面和上拉菜单的控制
    [application beginReceivingRemoteControlEvents];

    return YES;
}

- (void)applicationWillTerminate:(UIApplication *)application
{
    // 在App要终止前结束接收远程控制事件, 也可以在需要终止时调用该方法终止
    [application endReceivingRemoteControlEvents];
}

// 在具体的控制器或其它类中捕获处理远程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event
{
    // 根据事件的子类型(subtype) 来判断具体的事件类型, 并做出处理
    switch (event.subtype) {
        case UIEventSubtypeRemoteControlPlay:
        case UIEventSubtypeRemoteControlPause: {
            // 执行播放或暂停的相关操作 (锁屏界面和上拉快捷功能菜单处的播放按钮)
            break;
        }
        case UIEventSubtypeRemoteControlPreviousTrack: {
            // 执行上一曲的相关操作 (锁屏界面和上拉快捷功能菜单处的上一曲按钮)
            break;
        }
        case UIEventSubtypeRemoteControlNextTrack: {
            // 执行下一曲的相关操作 (锁屏界面和上拉快捷功能菜单处的下一曲按钮)
            break;
        }
        case UIEventSubtypeRemoteControlTogglePlayPause: {
            // 进行播放/暂停的相关操作 (耳机的播放/暂停按钮)
            break;
        }
        default:
            break;
    }
}

这里由于系统iOS13的适配,MPCommand的结果往往都需要返回一个MPRemoteCommandHandlerStatus,开发者需要注意,不然会有偶发的崩溃。

MPRemoteCommandCenter

我们使用iOS 7.1之后系统提供的MPRemoteCommandCenter

- (void)startCommandMonitor {
    dispatch_async(dispatch_get_main_queue(), ^{
        [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    });
    
    MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
    commandCenter.pauseCommand.enabled = YES;
    [commandCenter.pauseCommand addTarget:self action:@selector(commandPause)];
    commandCenter.playCommand.enabled = YES;
    [commandCenter.playCommand addTarget:self action:@selector(commandPlay)];
}
  • beginReceivingRemoteControlEvents必须要在主线程调用
  • MPRemoteCommand要起作用的话,我这里设置的ModeAVAudioSessionCategoryPlaybackAVAudioSessionCategoryPlayAndRecord模式下回调是不走的。
- (MPRemoteCommandHandlerStatus)commandPause {
    BOOL result = [self pause];
    if (result) {
        return MPRemoteCommandHandlerStatusSuccess;
    }else {
        return MPRemoteCommandHandlerStatusCommandFailed;
    }
}

- (MPRemoteCommandHandlerStatus)commandPlay {
    BOOL result = [self resume];
    if (result) {
        return MPRemoteCommandHandlerStatusSuccess;
    }else {
        return MPRemoteCommandHandlerStatusCommandFailed;
    }
}

- (void)stopCommandMonitor {
    MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
    commandCenter.pauseCommand.enabled = NO;
    [commandCenter.pauseCommand removeTarget:self];
    commandCenter.playCommand.enabled = NO;
    [commandCenter.playCommand removeTarget:self];
    dispatch_async(dispatch_get_main_queue(), ^{
        [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    });
    
}

这样就处理了单耳机拿下pause的情景。

双耳机取下

双耳机取下属于routeChange通知的部分,代表设备已经断开。
进行监听通知:

    NSNotificationCenter *noficationCenter = [NSNotificationCenter defaultCenter];
    [noficationCenter addObserver: self
                         selector: @selector(handleRouteChange:)
                             name: AVAudioSessionRouteChangeNotification
                           object: session];

在回调中处理AVAudioSessionRouteChangeReasonOldDeviceUnavailableAVAudioSessionRouteChangeReasonNewDeviceAvailable的情况即可

- (void)handleRouteChange:(NSNotification *)notification {
    AVAudioSession *session = [AVAudioSession sharedInstance];
    NSString *seccReason = @"";
    NSInteger reason = [[[notification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
    //  AVAudioSessionRouteDescription* prevRoute = [[notification userInfo] objectForKey:AVAudioSessionRouteChangePreviousRouteKey];
    switch (reason) {
        case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
            seccReason = @"The route changed because no suitable route is now available for the specified category.";
            break;
        case AVAudioSessionRouteChangeReasonWakeFromSleep:
            seccReason = @"The route changed when the device woke up from sleep.";
            break;
        case AVAudioSessionRouteChangeReasonRouteConfigurationChange:
            seccReason = @"The output route configuration changed.";
            break;
        case AVAudioSessionRouteChangeReasonOverride:
            seccReason = @"The output route was overridden by the app.";
            break;
        case AVAudioSessionRouteChangeReasonCategoryChange:{
            seccReason = @"The output route category changed.";  
        }
            break;
        case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:{
        //可以进行判断设备类型,这里仅pause了
        	[self pause];
        }
        	break;
        case AVAudioSessionRouteChangeReasonNewDeviceAvailable:{
        //可以进行判断设备类型
        	[self resume];
        }
        	break;
        case AVAudioSessionRouteChangeReasonUnknown:{
            seccReason = [NSString stringWithFormat:@"AVAudioSession Route change Reason is %ld (oldUnavailiable:2,newDevice:1,unknown:0)",(long)reason];
        }
            break;
        default:
            seccReason = [NSString stringWithFormat:@"The reason invalidate enum value : %ld",(long)reason];
            break;
    }
    
    AVAudioSessionRouteDescription *currentRoute = session.currentRoute;
    for (AVAudioSessionPortDescription *output in currentRoute.outputs) {
        if (output.portType == AVAudioSessionPortBluetoothA2DP || output.portType == AVAudioSessionPortBluetoothLE || output.portType == AVAudioSessionPortBluetoothHFP ) { //耳机
            _isBlueTooth = YES;
        }else {
            _isBlueTooth = NO;
        }
    }
    GSLog(@"handleRouteChange reason is %@,mode:%@,category:%@", seccReason,session.mode,session.category);
    
}

这里需要注意的以下几点:

  • beginReceivingRemoteControlEvents必须要在主线程调用
  • MPRemoteCommand要起作用的话,我这里设置的CategoryAVAudioSessionCategoryPlaybackAVAudioSessionCategoryPlayAndRecord模式下回调是不走的。你可以进行两种Category的切换来达到你想要的效果。 微信在开启视频语音时,也是无法响应airpods的事件的。
  • 事件返回类型需要是MPRemoteCommandHandlerStatus
  • 双击下一首等事件处理和pause事件一样,参考下面的文章就好了

参考文章:
http://blog.cocoachina.com/article/29610
https://blog.csdn.net/shengpeng3344/article/details/96424515


有时候人就喜欢给自己添堵啊,有些东西别招惹,有些人你撼动不了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值