I have a video player that can play from the iOS command center and lock screen. When I toggle a play/pause button in my app, it should update the play/pause button in the command center (MPRemoteCommandCenter
) by updating the nowPlayingInfo
(MPNowPlayingInfoCenter
). I'm not sure why it's not updating.
For example, if I pause the video with a custom button in my app, the command center still shows the pause button (meaning the video is still playing which is wrong.)
This is how I update the nowPlayingInfo
:
func updateMPNowPlayingInforCenterMetadata() {
guard video != nil else {
nowPlayingInfoCenter.nowPlayingInfo = nil
return
}
var nowPlayingInfo = nowPlayingInfoCenter.nowPlayingInfo ?? [String: Any]()
let image: UIImage
if let placeholderLocalURL = video.placeholderLocalURL, let placeholderImage = UIImage(contentsOfFile: placeholderLocalURL.path) {
image = placeholderImage
} else {
image = UIImage()
}
let artwork = MPMediaItemArtwork(boundsSize: image.size, requestHandler: { _ -> UIImage in
return image
})
nowPlayingInfo[MPMediaItemPropertyTitle] = video.title
nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = video.creator?.name ?? " "
nowPlayingInfo[MPMediaItemPropertyArtwork] = artwork
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = Float(video.duration)
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = Float(currentTime) // CMTimeGetSeconds(player.currentItem!.currentTime())
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = player.rate
nowPlayingInfo[MPNowPlayingInfoPropertyDefaultPlaybackRate] = player.rate
nowPlayingInfoCenter.nowPlayingInfo = nowPlayingInfo
if player.rate == 0.0 {
state = .paused
} else {
state = .playing
}
}
With KVO, when the player's rate
changes, I call this function:
// MARK: - Key-Value Observing Method
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard context == &assetPlaybackManagerKVOContext else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
} else if keyPath == #keyPath(AVPlayer.rate) {
updateMPNowPlayingInforCenterMetadata()
}
}
Any thoughts?
UPDATE
I found a solution though but not perfect in my case. So in my app I have 2 view controller's. Let's call them FeedVC
and PlayerVC
. So FeedVC
has AVPlayer
's that are always playing but are muted. If you click on one of them, then the PlayerVC
is created and plays the full video. If I pause the AVPlayer
's in FeedVC
before going to PlayerVC
then the "play/pause" button in the NowPlayingInfoCenter works perfectly!
Is there a way to make this work without having to pause the videos in the FeedVC
?
Another issue is that the elapsed time
keeps counting if I don't pause the players in the FeedVC
. It seems that if multiple players are playing, the play/pause button and elapsed time are incorrect.
When you setting the dictionary for
nowPlayingInfo
you will want to set theMPNowPlayingInfoPropertyPlaybackRate
value appropriately. TheMPNowPlayingInfoCenter
is expecting either a1.0
(playing) or0.0
(not playing) value as aDouble
wrapped in aNSNumber
object. Below you will find the code for how I'm setting thenowPlayingInfo
in my project.In this method I am passing the
song
that my player is has currently loaded. Whenever the user chooses to play or pause I callsetNowPlayingMediaWith(song:currentTime:)
to update the device's control console.I keep track of the
currentTime
(Double
) as a property of my player. If there is acurrentTime
passed in that means we are meant to be playing, so set theMPNowPlayingInfoPropertyPlaybackRate
to1.0
and set theMPNowPlayingInfoPropertyElapsedPlaybackTime
tocurrentTime
. This will set the current time to start automatically playing in the device's control console.Likewise, if there is no
currentTime
passed then we have stopped or paused. In this case we set theMPNowPlayingInfoPropertyPlaybackRate
to0.0
and we do not include theMPNowPlayingInfoPropertyElapsedPlaybackTime
.Download my app to see this in action.
EDIT (answer to comments)
"Is there a way to make this work without having to pause the videos in the FeedVC"
Without seeing your code it is difficult to give you a definite answer. It would make sense though to pause any ongoing media before starting your
PlayerVC
's media."the elapsed time keeps counting"
The elapsed time will countdown the elapsed time based on an
NSTimer
property onNowPlayingInfoCenter
. This timer will stop only when the timers value has reached the value you set inMPMediaItemPropertyPlaybackDuration
, or when you update theMPNowPlayingInfoPropertyElapsedPlaybackTime
.My suggestion is to write a method that "clears out" the
NowPlayingInfoCenter
. This method should set the will set all of key values to either 0.0 or nil respectively. Call this "clear out" method each time before you play your media inPlayerVC
. Then once you play fromPlayerVC
, set theNowPlayingInfoCenter
key values like you would in the method I pasted in my answer to set the new values for the new playing media.