Reputation: 180
When embedding an AVPlayerViewController in a portrait only iOS app it seems the app can get stuck in a weird layout when the player exits full screen if the video is full-screened while the device is held in a landscape orientation.
Is this a bug or am I doing something incorrectly?
Here's how to reproduce with a clean project using Xcode 9.4.1, swift 4, iOS 11.4, simulator or physical device.
ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
//Create the player and add as child view controller
let playerVC = AVPlayerViewController()
self.addChildViewController(playerVC)
//Place player's view in self
playerVC.view.frame = CGRect(x: 10, y: 40, width: 355, height: 200)
self.view.addSubview(playerVC.view)
//Load example video
playerVC.player = AVPlayer(url: URL(string: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4")!)
}
How it works normally:
How it breaks:
Upvotes: 6
Views: 8153
Reputation: 1
I tried to achieve using willTransitionToTraitCollection method. I have a viewController which has reference of AVPlayerViewController and a AVPlayer and inline playerView 1.Lock the orientation to allButUpsideDown 2. On load, load the AVPlayerViewController as a child and snap it to PlayerView 3. And override willTransitionToNewTraitCollection method
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
super.willTransition(to: newCollection, with: coordinator)
coordinator.animate(alongsideTransition: { [unowned self] _ in
if newCollection.verticalSizeClass == .compact {
self.videoPlayerController.willMove(toParent: nil)
self.videoPlayerController.view.removeFromSuperview()
self.videoPlayerController.removeFromParent()
DispatchQueue.main.async {
self.present(self.videoPlayerController,
animated: false) {
// Do some stuff if needed
}
}
}
else if newCollection.verticalSizeClass == .regular {
self.dismiss(animated: false) {
DispatchQueue.main.async {
self.videoPlayerController.view.frame = self.playerView.bounds
self.playerView.addSubview(self.videoPlayerController.view)
}
}
}
}) { _ in
// Lock the orientation to allButUpSideDown after animation.
}
}
Upvotes: 0
Reputation: 192
When you leave player full screen you enter in viewWillApper of your View Controller. So in viewWillAppear try to set your window frame to be equal to your screen bounds.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
appDelegate.window?.rootViewController?.view.frame = UIScreen.main.bounds
}
Upvotes: 4
Reputation: 51
You can identify when the player become fullscreen and force the portrait or landscape for your view controller.
It is not the best solution, but at least avoids the broken UI:
import AVFoundation
import AVKit
var isPlayerFullscreen: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
//Create the player and add as child view controller
let playerVC = AVPlayerViewController()
self.addChildViewController(playerVC)
//Place player's view in self
playerVC.view.frame = CGRect(x: 10, y: 40, width: 355, height: 200)
self.view.addSubview(playerVC.view)
//Load example video
playerVC.player = AVPlayer(url: URL(string: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4")!)
//Add an observer for playerVC object
playerVC.addObserver(self, forKeyPath: "videoBounds", options: NSKeyValueObservingOptions.new, context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
{
if keyPath == "videoBounds", let rect = change?[.newKey] as? NSValue
{
let playerRect: CGRect = rect.cgRectValue
if playerRect.size.height <= 200 {
print("Video not in full screen")
UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
isPlayerFullscreen = false
} else {
print("Video in full screen")
isPlayerFullscreen = true
}
}
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return isPlayerFullscreen ? UIInterfaceOrientationMask.landscape: UIInterfaceOrientationMask.portrait
}
override var shouldAutorotate: Bool {
return true
}
Upvotes: 0