У меня есть метод, который изменяет звуковую дорожку, воспроизводимую моим приложением AVPlayer
, а также устанавливает MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo
для новой дорожки:
func setTrackNumber(trackNum: Int) {
self.trackNum = trackNum
player.replaceCurrentItemWithPlayerItem(tracks[trackNum])
var nowPlayingInfo: [String: AnyObject] = [ : ]
nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = tracks[trackNum].albumTitle
nowPlayingInfo[MPMediaItemPropertyTitle] = "Track \(trackNum)"
...
MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo = nowPlayingInfo
print("Now playing local: \(nowPlayingInfo)")
print("Now playing lock screen: \(MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo)")
}
Я вызываю этот метод, когда пользователь явно выбирает альбом или трек, а также когда трек заканчивается и автоматически начинается следующий. Экран блокировки правильно отображает метаданные дорожки, когда пользователь устанавливает альбом или дорожку, но НЕ когда дорожка заканчивается и автоматически устанавливается следующая.
Я добавил операторы печати, чтобы убедиться, что я правильно заполняю словарь nowPlayingInfo
. Как и ожидалось, два оператора печати печатают одно и то же содержимое словаря, когда этот метод вызывается для инициированного пользователем изменения альбома или дорожки. Однако в случае, когда метод вызывается после автоматической смены дорожки, локальная переменная nowPlayingInfo
показывает новую trackNum
, тогда как MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo
показывает предыдущую trackNum
:
Now playing local: ["title": Track 9, "albumTitle": Test Album, ...]
Now playing set: Optional(["title": Track 8, "albumTitle": Test Album, ...]
Я обнаружил, что когда я устанавливаю точку останова в строке, которая устанавливает MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo
в nowPlayingInfo
, то номер дорожки корректно обновляется на экране блокировки. Добавление sleep(1)
сразу после этой строки также гарантирует правильное обновление дорожки на экране блокировки.
Я проверил, что nowPlayingInfo
всегда устанавливается из основной очереди. Я пытался явно запустить этот код в основной очереди или в другой очереди без каких-либо изменений в поведении.
Что мешает мне перейти на MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo
? Как сделать так, чтобы параметр MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo
всегда обновлял информацию на экране блокировки?
ИЗМЕНИТЬ
После просмотра кода в N-й раз, думая о «параллелизме», я нашел виновника. Не знаю, почему я не заподозрил этого раньше:
func playerTimeJumped() {
let currentTime = currentItem().currentTime()
dispatch_async(dispatch_get_main_queue()) {
MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo?[MPNowPlayingInfoPropertyElapsedPlaybackTime] = CMTimeGetSeconds(currentTime)
}
}
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: "playerTimeJumped",
name: AVPlayerItemTimeJumpedNotification,
object: nil)
Этот код обновляет время, прошедшее с экрана блокировки, когда пользователь прокручивает или пропускает вперед/назад. Если я закомментирую это, обновление nowPlayingInfo
от setTrackNumber
будет работать как положено при любых условиях.
Пересмотренные вопросы: как взаимодействуют эти два фрагмента кода, когда они оба выполняются в основной очереди? Могу ли я каким-либо образом обновить nowPlayingInfo
на AVPlayerItemTimeJumpedNotification
, учитывая, что будет скачок при поступлении вызова на setTrackNumber
?
nowPlayingInfo
, скорее всего, выполняется асинхронно, поэтому отпечатки не так уж полезны. Но я ожидаю, что набор будет завершен в какой-то момент! Когда телефон показывает экран блокировки, я знаю, что всегда вызываетсяsetTrackNumber
, потому что мои отладочные отпечатки отображаются на консоли. - person Hélène Martin   schedule 04.02.2016