【问题标题】:iOS 4: Remote controls for background audioiOS 4:背景音频的遥控器
【发布时间】:2011-03-28 06:03:46
【问题描述】:

我目前正在尝试为我正在为 iOS 4 开发的应用程序设置背景音频。但是,该应用程序没有专用的音乐播放器 viewController,但与 Pandora 等其他背景音频应用程序不同,使任务更加混乱。

我已经正确设置了适当的Info.plist 设置,并且在我的应用程序委托中有一个AVAudioPlayer 对象,可以从任何地方访问。当用户播放歌曲时,我将AVAudioPlayer 替换为用歌曲初始化的新歌曲并播放。这一切都很好,除了现在我不知道如何支持远程控制事件。

根据 Apple 的文档,我有这个:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    [self becomeFirstResponder];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    [self resignFirstResponder];
}

- (BOOL)canBecomeFirstResponder {
    return YES;
}

- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
    switch(event.subtype) {
        case UIEventSubtypeRemoteControlTogglePlayPause:
            if([iPhoneAppDelegate backgroundAudioPlayer].playing)
                [iPhoneAppDelegate pauseBackgroundAudioPlayer];
            else
                [iPhoneAppDelegate playBackgroundAudioPlayer];
            break;
    }
}

问题是,我把这个放在哪里? Apple 的文档似乎建议这应该放在某个视图控制器中,但我的应用程序有很多视图控制器和导航控制器。无论我试图把它放在哪里,由于某种原因,点击多任务托盘遥控器中的切换播放/暂停按钮要么导致歌曲暂停片刻然后取消暂停,要么以某种方式导致歌曲播放两次。

【问题讨论】:

  • 你如何更改多任务栏中的播放/停止按钮?!

标签: iphone audio ios4 avfoundation


【解决方案1】:

似乎影响此行为的一件事是您使用 setCategory:withOptions:error: 为 AVAudioSession 设置的任何类别选项,而不仅仅是 setCategory:error:。特别是,从反复试验来看,如果您设置 AVAudioSessionCategoryOptionMixWithOthers ,您将不会获得远程控制事件;正在播放的控件仍将控制 iPod 应用程序。如果您设置 AVAudioSessionCategoryOptionDuckOthers ,您将获得远程控制事件,但似乎对于控制哪个应用程序可能存在一些歧义。将 categoryOptions 设置为 0 或只调用 setCategory:error: 效果最好。

【讨论】:

    【解决方案2】:
    【解决方案3】:

    我在这个问题上挣扎了一段时间,但上面的答案都没有奏效。我的代码中的错误,我希望它能帮助阅读本文的人,是我将 AudioSession 设置为与其他人混合。您想成为获取远程控制事件的前台音频播放器。检查您是否有这样的错误代码:

        [[AVAudioSession sharedInstance] setDelegate: self];
        [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: nil];
        UInt32 doSetProperty = 0;
        AudioSessionSetProperty (
                                 kAudioSessionProperty_OverrideCategoryMixWithOthers,
                                 sizeof (doSetProperty),
                                 &doSetProperty
                                 );
        NSError *activationError = nil;
        [[AVAudioSession sharedInstance] setActive: YES error: &activationError];       
    

    并移除 AudioSessionSetProperty,或将 doSetProperty 更改为 1。

    【讨论】:

      【解决方案4】:

      文档示例有点误导,但没有必要在任何地方子类化任何东西。放置 remoteControlReceivedWithEvent: 的正确位置是在应用程序委托中,因为无论应用程序是否在前台,它都保留在响应者链中。此外,开始/结束接收远程控制事件应该基于您是否真的需要这些事件,而不是基于某些随机视图的可见性。

      【讨论】:

      • 完全正确。要完全控制您的应用程序的远程控制(无论是在前台还是后台),您需要在 application:didFinishLaunchingWithOptions: 中调用 beginReceivingRemoteControlEvents,但这还不是全部。在您的应用告诉 iOS 它正在播放曲目后,您的 UIApplicationDelegate 开始接收远程控制事件 - 通过设置 MPNowPlayingInfoCenter nowPlayingInfo 属性。
      • 这个答案真的很棒,我被耽搁了一个星期。
      • 将 remoteControlReceivedWithEvent: 放入应用程序委托对我来说不起作用;我不得不继承 UIWindow。
      • 提示:remoteControlReceivedWithEvent 在我从 UIViewController 中删除重复项之前无法正常工作。
      • 将 remoteControlReceivedWithEvent: 放入应用程序委托对我也不起作用,直到我意识到我的应用程序委托(在 iOS2 时代从 xcode 模板创建)是 NSObject 的子类。一旦我将我的应用程序委托切换到 UIResponder -remoteControlReceivedWithEvent 的子类:开始被调用。
      【解决方案5】:

      无需继承 Window 或转发事件。只需从您的主视图控制器处理它。有关详细信息,请参阅混音器 (MixerHost) 示例。

      http://developer.apple.com/LIBRARY/IOS/#samplecode/MixerHost/Listings/Classes_MixerHostViewController_m.html

      【讨论】:

      • 问题是,当应用程序进入后台时,您的主视图控制器并不总是在屏幕上,也就是说,如果屏幕上有模式,它不会成为第一响应者。
      • @pablasso 可能是,具体取决于应用程序。但并非在所有情况下,我都同意。
      【解决方案6】:

      第二种方法对我不起作用,sendEvent 从未被调用过。但是第一种方法效果很好(子类化UIWindow)。

      【讨论】:

      • 像魅力一样工作。我想补充一点,可以在应用委托内部调用 beginReceivingRemoteControlEvents 来订阅远程控制事件。
      【解决方案7】:

      经过一番搜索,我在 Apple 开发者论坛上找到了几种接收全局远程控制事件的解决方案。

      一种方法是继承UIWindow 并覆盖其remoteControlReceivedWithEvent:

      第二种可能更好的方法是继承UIApplication 并覆盖sendEvent:。这样,您就可以拦截所有远程控制事件并在全局范围内处理它们,而不会让任何其他响应者在响应者链中稍后处理它们。

      - (void)sendEvent:(UIEvent *)event {
           if (event.type == UIEventTypeRemoteControl) {
                // Handle event
           }
           else
                [super sendEvent:event];
      }
      

      【讨论】:

      • 查看我对 Brian Dam Pedersen 回答的评论
      • 一个更好的方法是将它添加到一个类别中的普通 UIApplication
      • 我会推荐布赖恩的答案
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-19
      • 2014-04-30
      相关资源
      最近更新 更多