使用MPNowPlayingInfoCenter处理CarPlay中的播放事件

时间:2018-10-15 13:44:01

标签: ios swift mpnowplayinginfocenter carplay mpplayablecontentdelegate

我正在尝试使用CarPlay集成构建示例音频应用程序。该应用程序是一个测试项目-没有API,没有流媒体,没有复杂的UI。只是歌曲标题的简短列表,它具有选择并播放歌曲的功能。我的目标是在“正在播放”屏幕上按下“播放”按钮并处理文件时处理回调。

设置MPPlayableContentManager,MPPlayableContentDataSource和MPPlayableContentDelegate没有任何问题。我的内容是从JSON文件解析的,并且可以正确显示在模拟器的屏幕上。

// MARK: - MPPlayableContentDataSource methods
extension PlayableContentManager: MPPlayableContentDataSource {
  func numberOfChildItems(at indexPath: IndexPath) -> Int {
    if indexPath.count == 0 {
      return playlists.count
    } else if indexPath.count == 1 {
      return playlists[indexPath.first ?? 0].playlist.count
    } else if indexPath.count == 2 {
      return playlists.last?.playlist.count ?? 0
    }

    return 0
 }

  func contentItem(at indexPath: IndexPath) -> MPContentItem? {
    if indexPath.count == 1 {
      return createTabbar(item: playlists[indexPath.first ?? 
0].name.capitalized, itemType: playlists[indexPath.first ?? 0].type)
    } else if indexPath.count == 2 {
      if indexPath.first == 0 {
        return createContent(item: playlists[0].playlist[indexPath.last 
?? 0], isContainer: false, isPlayable: true)
      } else {
        return createContainerContent(item: playlists[indexPath.first 
?? 0].playlist[indexPath.last ?? 0])
      }
    }

    return createTabbar(item: "", itemType: nil)
  }
}

此代码提供以下图形输出:

CarPlay simulator

两个选项卡包含两个播放列表。每个播放列表都有许多歌曲。

当我轻按一首歌曲时,该应用程序将成为“正在播放”应用程序,但前提是在用户与该行进行交互时,我已经开始和结束接收远程控制事件。

当检测到单击表行时,将调用 initiatePlaybackOfContentItemAt 方法。

// MARK: - MPPlayableContentDelegate methods
extension PlayableContentManager: MPPlayableContentDelegate {
  func playableContentManager(_ contentManager: 
MPPlayableContentManager, initiatePlaybackOfContentItemAt indexPath: 
 IndexPath, completionHandler: @escaping (Error?) -> Void) {
    DispatchQueue.main.async {
      UIApplication.shared.beginReceivingRemoteControlEvents()
      self.infoCenter.nowPlayingInfo = self.setupNowPlayingInfo(for: 
indexPath)
      completionHandler(nil)
      UIApplication.shared.endReceivingRemoteControlEvents()
    }
  }
}

如果我希望应用程序过渡到“正在播放”屏幕,这是唯一对我有用的代码。如果我将任何UIApplication方法放置在其他任何地方,则该应用都会退出以响应行触摸,并且不会进入“正在播放”屏幕。

但是,我正在猜测是因为我正在调用 endReceivingRemoteControlEvents(),所以无法获取不同事件的回调。设置了现在正在播放的信息,我可以在UI中看到播放按钮,但是当我按下它时,回调不会执行。

private func setupPlaybackCommands() {
    commandCenter = MPRemoteCommandCenter.shared()

    commandCenter.playCommand.addTarget { [unowned self] event in
      if self.audioPlayer.rate == 0.0 {
        self.play()
        return .success
      }
      return .commandFailed
    }
....
}

Now playing screen

我在做什么错?

这与我在模拟器上进行测试有关吗?这可以在真实设备上工作吗?

如果有人可以阐明如何正确设置CarPlay集成以进入“正在播放”屏幕并响应事件,请分享。我在查找任何可用的代码示例或示例时遇到很多麻烦。

我知道我可以,但我尚未申请CarPlay授权,因为该项目仅用于研究目的,我非常怀疑我会获得批准。

1 个答案:

答案 0 :(得分:1)

只有在快速暂停(例如一秒钟)后调用playCommand方法时,我才能“修复” stopCommandbeginReceivingRemoteControlEvents来接收回调:

extension PlayableContentManager: MPPlayableContentDelegate {
  func playableContentManager(_ contentManager: 
MPPlayableContentManager, initiatePlaybackOfContentItemAt indexPath: 
 IndexPath, completionHandler: @escaping (Error?) -> Void) {
    DispatchQueue.main.async {
      UIApplication.shared.beginReceivingRemoteControlEvents()
      self.infoCenter.nowPlayingInfo = self.setupNowPlayingInfo(for: 
indexPath)
      completionHandler(nil)
      UIApplication.shared.endReceivingRemoteControlEvents()

      // TODO: add 1 second timeout, and call this after
      UIApplication.shared.beginReceivingRemoteControlEvents()
    }
  }
}

另一件事,当用户导航到“正在播放”屏幕时,iOS假定用户必须点击“播放”按钮,然后才可以开始播放(通过处理远程命令回调) 。否则,您将被重定向到“正在播放”屏幕,并且音频处于活动状态,并且无法使按钮带有“停止”图标。

更新

虽然以上逻辑适用于Simulator,但我已经在真正的CarPlay设备上进行了测试,但这不是必需的。您开始播放并调用完成处理程序。 iOS会自动处理所有其余操作,包括过渡到“正在播放”屏幕。