9

I'd like to know if it is possible to detect when the playback controls appear or disappear from the AVPlayerViewController view. I'm trying to add a UI element on my player that must follow the playback controls display. Appearing only when the controls are displayed, disappearing otherwise

I don't seem to find any value that I can observe on AVPlayerViewController to achieve this nor any callbacks or delegate methods.

My project is in Swift.

Ahmad F
  • 30,560
  • 17
  • 97
  • 143
Martin
  • 843
  • 8
  • 17
  • It seems to be undoable for now. However, as a workaround you might check: https://stackoverflow.com/questions/39885018/in-swift-how-to-detect-touch-while-playing-video-in-avplayerviewcontroller/39885221#39885221 OR you might need to add subview(s) to its `contentOverlayView`. – Ahmad F Mar 12 '19 at 16:36
  • 1
    Thank you for the quick response. The problem of adding subviews to contentOverlayView is that they are always visible. They don't follow the display toggling occuring on the playback controls. – Martin Mar 12 '19 at 16:46
  • Yes that's true.So you might need to follow the approach of the question I mentioned, however keep in mind that it won't match exactly the showing/hiding the playback controls. At least that's the best we can do now :) – Ahmad F Mar 12 '19 at 16:49

1 Answers1

1

A simple way to observe and respond to the playback changes is to use key-value observing (KVO). In your case, observe AVPlayer's timeControlStatus or rate property.

e.g.:

{
  // 1. Setup AVPlayerViewController instance (playerViewController)

  // 2. Setup AVPlayer instance & assign it to playerViewController

  // 3. Register self as an observer of the player's `timeControlStatus` property

  // 3.1. Objectice-C
  [player addObserver:self
           forKeyPath:@"timeControlStatus"
              options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew // NSKeyValueObservingOptionOld is optional here
              context:NULL];

  // 3.2. Swift
  player.addObserver(self,
                     forKeyPath: #keyPath(AVPlayer.timeControlStatus),
                     options: [.old, .new], // .old is optional here
                     context: NULL)
}

To be notified of state changes, you implement the -observeValueForKeyPath:ofObject:change:context: method. This method is invoked whenever the timeControlStatus value changes.

// Objective-C
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary <NSKeyValueChangeKey, id> *)change
                       context:(void *)context
{
  if ([keyPath isEqualToString:@"timeControlStatus"]) {
    // Update your custom UI here depend on the value of `change[NSKeyValueChangeNewKey]`:
    // - AVPlayerTimeControlStatusPaused
    // - AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate
    // - AVPlayerTimeControlStatusPlaying
    AVPlayerTimeControlStatus timeControlStatus = (AVPlayerTimeControlStatus)[change[NSKeyValueChangeNewKey] integerValue];
    // ...

  } else {
    [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
  }
}

// Swift
override func observeValue(forKeyPath keyPath: String?,
                           of object: Any?,
                           change: [NSKeyValueChangeKey : Any]?,
                           context: UnsafeMutableRawPointer?)
{
  if keyPath == #keyPath(AVPlayer.timeControlStatus) {
    // Deal w/ `change?[.newKey]`
  } else {
    super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
  }
}

And last most important step, remember to remove the observer when you don't need it anymore, generally at -dealloc:

[playerViewController.player removeObserver:self forKeyPath:@"timeControlStatus"];

Btw, you can also observe AVPlayer's rate property, cause -play equivalents to setting the value of rate to 1.0, and -pause equivalent to setting the value of rate to 0.0.

But in your case, I think timeControlStatus makes more sense.


There's an official DOC for further reading (but just "Ready to Play", "Failed" & "Unknown" states, useless here): "Responding to Playback State Changes" .

Hope it helps.

Kjuly
  • 34,476
  • 22
  • 104
  • 118