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?
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?
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?
I also Experiencing the same problem. the avplayerviewcontroller in iOS 16 have no playback overlay
I ended up creating a button over the top of the video that looks pretty much identical to the one in iOS 15 that disappears when tapped and plays the video. It's a bit of a kludge, but I couldn't find any native way to achieve it in iOS 16.
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
worked for me as well thanks
Worked for me too, thanks.
@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")
})
}
The downside of using this work around is that you'll be using private api calls which can result in your app being rejected by Apple.
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()
}
Do you have a full example project? The code you have is working for me.
I have create an example App using my code example: https://github.com/renep/EmbeddedPlayer/
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.