Обработка событий воспроизведения в CarPlay с помощью MPNowPlayingInfoCenter
Я пытаюсь создать пример аудио-приложения с интеграцией CarPlay. Приложение представляет собой тестовый проект - без API, без потоковой передачи, без сложного пользовательского интерфейса. Просто краткий список названий песен с функцией выбора и воспроизведения. Моя цель - обработать обратный вызов при нажатии кнопки воспроизведения на экране "Сейчас исполняется" и воспроизвести файл.
У меня нет проблем с настройкой 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)
}
}
Этот код дает следующий графический вывод:
Две вкладки содержат два списка воспроизведения. В каждом плейлисте есть несколько песен.
Когда я нажимаю на песню, приложение становится приложением "Сейчас исполняется", но только в том случае, если я одновременно начал и закончил получать события дистанционного управления во время взаимодействия пользователя со строкой.
Метод 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 (), я не могу получить обратный вызов для различных событий. Теперь информация о воспроизведении установлена, я вижу кнопку воспроизведения в пользовательском интерфейсе, но когда я нажимаю ее, обратный вызов не выполняется.
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
}
....
}
Что я делаю неправильно?
Может ли это быть как-то связано с тем, что я тестирую на симуляторе? Будет ли это работать на реальном устройстве?
Если кто-то может пролить свет на то, как правильно настроить интеграцию CarPlay, чтобы перейти на экран "Сейчас исполняется" и ответить на события, поделитесь. У меня много проблем с поиском каких-либо полезных примеров кода или примеров.
Я знаю, что могу, но я не подал заявку на получение разрешения CarPlay, потому что этот проект предназначен только для исследовательских целей, и я очень сомневаюсь, что получу одобрение.
1 ответ
Я был в состоянии "исправить" 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()
}
}
}
Еще одна вещь, когда пользователь переходит на экран "Сейчас играет", iOS предполагает, что пользователь должен нажать кнопку "Воспроизвести", и только после этого вы должны начать воспроизведение (обработав обратный вызов удаленной команды). В противном случае вы будете перенаправлены на экран "Сейчас исполняется" с активным звуком и без возможности сделать кнопку со значком "Стоп".