Video player options not visible on iOS 16

We are using AVPlayerViewController to display/play video. Till iOS 15 video player options are visible but for iOS 16 it's not visible.

Do we require to make changes for iOS 16 to display video player options?

Answered by DTS Engineer in 759286022

Hello!

When you are embedding a view controller in a view hierarchy, it is important to make explicit calls to beginAppearanceTransition. From the docs:

If you are implementing a custom container controller, use this method to tell the child that its views are about to appear or disappear. Do not invoke viewWillAppear(:), viewWillDisappear(:), viewDidAppear(:), or viewDidDisappear(:) directly.

So, without these calls, those lifecycle methods are not called. In iOS 16, AVPlayerViewController moved its controls view setup into one of those "appear" lifecycle methods. Clients that were embedding AVPlayerViewController without making explicit calls to beginAppearanceTransition "lost" their controls.

Here is an example SwiftUI representable container that demonstrates how to implement this:

struct MyVideoPlayer: UIViewControllerRepresentable {
    
    let player: AVPlayer
        
    func makeUIViewController(context: Context) -> AVPlayerViewController {
        
        let controller = AVPlayerViewController()
        controller.player = player
        
        controller.beginAppearanceTransition(true, animated: false)
        
        return controller
    }
    
    func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context)  {}
    
    static func dismantleUIViewController(_ uiViewController: AVPlayerViewController, coordinator: ()) {
        uiViewController.beginAppearanceTransition(false, animated: false)
    }
}

Do you already have a solution for this problem?

Experiencing the same here, no controls, however there appears to be gestures configured as I'm able to play (by tapping the center of the video), and scrub (by tap-dragging across the video)

Likewise, I'm experiencing the same as well. Has anyone found a solution for this at all? I've tried numerous suggestions, but nothing seems to invoke the controls UI by default.

I also Experiencing the same problem. the avplayerviewcontroller in iOS 16 have no playback overlay

I had this problem too. I downloaded apple sample code.
https://developer.apple.com/documentation/avkit/playing_video_content_in_a_standard_user_interface This code works fine for iOS 16. Video player options are visible. I found differences with my code. I added only view from AVPlayerViewController, but I didn't add AVPlayerViewController

old code:

container.addSubview(playerViewController.view) 

new code:

parent.addChild(playerViewController)
container.addSubview(playerViewController.view) 

Now it works fine

@YuriKuzminsky that worked for me - well done!

How about SwiftUI? I have same problem with AVPlayer and AVPlayerViewController((

None of these solutions work for me on Xcode 14.0 and iOS 16. Taking the previous suggestion, I ran Apple's own AVKit sample against iOS 15.5 and iOS 16. Their code has the same issue: on iOS 16, you don't see any preview controls until you tap once on the player. That's definitely a change that wasn't communicated to the dev community, AFAIK.

For Swift UI -> IOS 16

CustomVideoPlayer() .compositingGroup() // Mandatory

struct CustomVideoPlayer : UIViewControllerRepresentable {

var player: AVPlayer

func makeUIViewController(context: UIViewControllerRepresentableContext) -> AVPlayerViewController {

 let controller = AVPlayerViewController()
 controller.player = player
 return controller

}

func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext) { }

try this kludge:

   func updateControlsvisibility(view: UIView?){
    guard let view = view else {
      return
    }
    if view.isKind(of: NSClassFromString("AVButton") ?? NSNull.self){
      view.alpha = 1.0
      view.isHidden = false
      var parent = view.superview
      while let p = parent{
        p.alpha = 1.0
        p.isHidden = false
        parent = p.superview
      }
      return
    }
    for subview in view.subviews{
      if subview != view {
        self.updateControlsvisibility(view: subview)
      }
    }
  }

//when the AVPlayerViewController is ready to display call this function
         playerViewController.observe(\.isReadyForDisplay) { [weak self] observed, _ in
          if observed.isReadyForDisplay {
            self?.updateControlsvisibility(view: playerViewController.view)
          }
        }

this will work for U:

  func updateControlsvisibility(view: UIView?){
    guard let view = view else {
      return
    }
    if view.isKind(of: NSClassFromString("AVButton") ?? NSNull.self){
      view.alpha = 1.0
      view.isHidden = false
      var parent = view.superview
      while let p = parent{
        p.alpha = 1.0
        p.isHidden = false
        parent = p.superview
      }
      return
    }
    for subview in view.subviews{
      if subview != view {
        self.updateControlsvisibility(view: subview)
      }
    }
  }

//when the AVPlayerViewController is ready to display call this function
 playerViewController.observe(\.isReadyForDisplay) { [weak self] observed, _ in
    if observed.isReadyForDisplay, greateThaniOS16 {
    self?.updateControlsvisibility(view: playerViewController.view) 
    }
}

I think a work around for this issue can be adding

if #available(iOS 16.0, *) {
    avPlayerViewController.setValue(false, forKey: "canHidePlaybackControls")
}

and removing it after one second. So the code will look like something like this:

let avPlayer: AVPlayer = AVPlayer(url: newURL)
avPlayerViewController = AVPlayerViewController()
if let avPlayerViewController = avPlayerViewController {
    avPlayerViewController.delegate = self
    avPlayerViewController.player = avPlayer
    if #available(iOS 16.0, *) {
        avPlayerViewController.setValue(false, forKey: "canHidePlaybackControls")
    }
    addChild(avPlayerViewController)
    view.addSubview(avPlayerViewController.view)
}
if #available(iOS 16.0, *) {
    DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
        self.avPlayerViewController?.setValue(true, forKey: "canHidePlaybackControls")
    })
}

Hello!

When you are embedding a view controller in a view hierarchy, it is important to make explicit calls to beginAppearanceTransition. From the docs:

If you are implementing a custom container controller, use this method to tell the child that its views are about to appear or disappear. Do not invoke viewWillAppear(:), viewWillDisappear(:), viewDidAppear(:), or viewDidDisappear(:) directly.

So, without these calls, those lifecycle methods are not called. In iOS 16, AVPlayerViewController moved its controls view setup into one of those "appear" lifecycle methods. Clients that were embedding AVPlayerViewController without making explicit calls to beginAppearanceTransition "lost" their controls.

Here is an example SwiftUI representable container that demonstrates how to implement this:

struct MyVideoPlayer: UIViewControllerRepresentable {
    
    let player: AVPlayer
        
    func makeUIViewController(context: Context) -> AVPlayerViewController {
        
        let controller = AVPlayerViewController()
        controller.player = player
        
        controller.beginAppearanceTransition(true, animated: false)
        
        return controller
    }
    
    func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context)  {}
    
    static func dismantleUIViewController(_ uiViewController: AVPlayerViewController, coordinator: ()) {
        uiViewController.beginAppearanceTransition(false, animated: false)
    }
}

The recommendation does not work for me. I have embedded the AVPlayerViewController in a UIViewController, also the beginAppearanceTransition is called, but no video controls are shown. They are shown when the video is tabbed, but not initially. I don't want to add the workaround suggested by nbeanm but I have not found any other solution yet.

Here is my code:


	func addPlayer(mediaURL: URL) {
		let playerController = AVPlayerViewController()
		playerController.showsTimecodes = true
		playerController.player = AVPlayer(url: mediaURL)
		playerController.beginAppearanceTransition(true, animated: false)
		self.addChild(playerController)
		view.addSubview(playerController.view)

		// Setup the layout. I have created my own layout helper for setting up layout constraints.
		// It should be obvious, that the player uses all the space of it's parent view.
		// My layout helper can be found here: https://github.com/openbakery/PinLayout/
		playerController.view.layout.pin(.top, .bottom, .leading, .trailing)

		playerController.didMove(toParent: self)
		self.actualPreviewController = playerController
		playerController.endAppearanceTransition()
	}


Video player options not visible on iOS 16
 
 
Q