使用 MPNowPlayingInfoCenter 处理 CarPlay 中的播放事件
Handling playback events in CarPlay with MPNowPlayingInfoCenter
我正在尝试构建一个带有 CarPlay 集成的示例音频应用程序。该应用程序是一个测试项目 - 没有 API,没有流媒体,没有复杂的 UI。只是一个简短的歌曲名称列表,具有 select 一首并播放的功能。我的目标是在“正在播放”屏幕上按下播放按钮并播放文件时处理回调。
我在设置 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
这两个选项卡包含两个播放列表。每个播放列表都有许多歌曲。
当我点击一首歌曲时,该应用会变成“正在播放”应用,但前提是我在用户与该行交互时同时开始和结束接收远程控制事件。
当检测到单击 table 行时,将调用 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 权利,因为这个项目仅用于研究目的,我非常怀疑我是否会获得批准。
我能够 "fix" playCommand
和 stopCommand
仅在快速暂停(如一秒)后调用 beginReceivingRemoteControlEvents
方法时才能接收回调:
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()
}
}
}
还有一件事,当用户导航到 "Now Playing" 屏幕时,iOS 假定用户必须点击 "Play" 按钮,然后才应该开始播放(通过处理远程命令回调)。否则,您将被重定向到具有活动音频的 "Now Playing" 屏幕,并且无法使用 "Stop" 图标制作按钮。
更新
虽然上述逻辑适用于模拟器,但我已经在真实的 CarPlay 设备上进行了测试,这不是必需的。您开始播放并调用完成处理程序。 iOS 自动处理所有其余部分,包括过渡到 "Now Playing" 屏幕。
我正在尝试构建一个带有 CarPlay 集成的示例音频应用程序。该应用程序是一个测试项目 - 没有 API,没有流媒体,没有复杂的 UI。只是一个简短的歌曲名称列表,具有 select 一首并播放的功能。我的目标是在“正在播放”屏幕上按下播放按钮并播放文件时处理回调。
我在设置 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
这两个选项卡包含两个播放列表。每个播放列表都有许多歌曲。
当我点击一首歌曲时,该应用会变成“正在播放”应用,但前提是我在用户与该行交互时同时开始和结束接收远程控制事件。
当检测到单击 table 行时,将调用 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 权利,因为这个项目仅用于研究目的,我非常怀疑我是否会获得批准。
我能够 "fix" playCommand
和 stopCommand
仅在快速暂停(如一秒)后调用 beginReceivingRemoteControlEvents
方法时才能接收回调:
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()
}
}
}
还有一件事,当用户导航到 "Now Playing" 屏幕时,iOS 假定用户必须点击 "Play" 按钮,然后才应该开始播放(通过处理远程命令回调)。否则,您将被重定向到具有活动音频的 "Now Playing" 屏幕,并且无法使用 "Stop" 图标制作按钮。
更新
虽然上述逻辑适用于模拟器,但我已经在真实的 CarPlay 设备上进行了测试,这不是必需的。您开始播放并调用完成处理程序。 iOS 自动处理所有其余部分,包括过渡到 "Now Playing" 屏幕。