Taytee13
Taytee13

Reputation: 131

How to force landscape mode in AVPlayer?

I'm looking for a way to force an AVplayer (Video) to only display itself in landscape mode (home button on the left). Is this possible?

EDIT: Attempted to add playerViewController.supportedInterfaceOrientations = .landscapeLeft

Got error message "Cannot assign to property: 'supportedInterfaceOrientations' is a get-only property"

import AVKit
import AVFoundation

UIViewController {
    var videoPlayer = AVPlayer()
    var playerViewController = AVPlayerViewController()

   override func viewDidAppear(_ animated: Bool) {
     super.viewDidAppear(animated)
     play()
   }

   func play() {
     playerViewController.supportedInterfaceOrientations = .landscapeLeft
     let moviePath = Bundle.main.path(forResource: "full video", ofType: "mov")
     if let path = moviePath {
        let url = NSURL.fileURL(withPath: path)
        let videoPlayer = AVPlayer(url: url)
        let playerViewController = AVPlayerViewController()
        playerViewController.player = videoPlayer
        self.present(playerViewController, animated: true) {
            if let validPlayer = playerViewController.player {
                validPlayer.play()
                playerViewController.showsPlaybackControls = false

               }
            }
        }
    }
}

EDIT: Attempted to add new subclass of AVPlayerViewController

import UIKit
import AVFoundation
import AVKit

class LandscapeVideoControllerViewController: AVPlayerViewController {

  override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return .landscapeLeft
  }

}

but getting error message "Method does not override any method from its superclass"

Upvotes: 10

Views: 15372

Answers (4)

Ryan Haycock
Ryan Haycock

Reputation: 73

I came across this problem recently and posted a similar solution here modifying Zell B.'s answer.

Here is a Swift 5 version that will detect an AVPlayer and update the orientation settings. It doesn't involve sub-classing AVPlayerViewController so it avoids any unexpected behaviour.

Just copy and paste the below code into your AppDelegate (no editing necessary):

func application(_ application: UIApplication, 
                 supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {

    if fetchCurrentVC(self.window?.rootViewController) is AVPlayerViewController {
        return .allButUpsideDown
    }
    return .portrait
}

func fetchCurrentVC(_ viewController: UIViewController?) -> UIViewController? {

    if let tabBarController = viewController as? UITabBarController {
        return fetchCurrentVC(tabBarController.selectedViewController)
    }

    if let navigationController = viewController as? UINavigationController{
        return fetchCurrentVC(navigationController.visibleViewController)
    }

    if let viewController = viewController?.presentedViewController {
        return fetchCurrentVC(viewController)
    }

    return viewController
}

You could modify .landscapeLeft to any orientation you require.

Upvotes: 1

CristianMoisei
CristianMoisei

Reputation: 2279

Since Apple warns not to subclass AVPlayerViewController, a simpler solution I found is to change the orientation of the app to .landscape before presenting the instance of AVPlayerViewController, and changing it back when it's dismissed.

1. In App Delegate

We set the supported orientations for the app. In my case, the app only supports .portrait, but it could be set to .all if needed

class AppDelegate: UIResponder, UIApplicationDelegate {
    var orientation: UIInterfaceOrientationMask = .portrait
    func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
        return orientation
    }
}    

2. In the VC presenting the AVPlayerViewController

We'll use the completion handler of the present method to change the orientation of playerController to landscape.

class VCThatOpensTheVideo: UIViewController {

    let appDelegate = UIApplication.shared.delegate as! AppDelegate


    @IBAction func tappedPlayVideo(_ sender: Any) {
        guard let path = Bundle.main.path(forResource: "video-name", ofType:"mp4") else { return }
    
        let playerController = AVPlayerViewController()
        let player = AVPlayer(url: URL(fileURLWithPath: path))
    
        playerController.player = player
    
        present(playerController, animated: true) {
            player.play() // Optionally autoplay the video when it starts
            self.appDelegate.orientation = .landscape
        UIDevice.current.setValue(UIInterfaceOrientation.landscapeLeft.rawValue, forKey: "orientation")
        }
    }

}

Then we override the viewWillAppear method of the presenting VC to return back to .portrait

class VCThatOpensTheVideo: UIViewController {

    override func viewWillAppear(_ animated: Bool) {
        appDelegate.orientation = .portrait
        UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
    }
}

Upvotes: 1

Hitesh Surani
Hitesh Surani

Reputation: 13537

Just create sub-class of AVPlayerViewController

import AVKit

class LandscapePlayer: AVPlayerViewController {
      override var supportedInterfaceOrientations: UIInterfaceOrientationMask{
          return .landscapeLeft
       }
}

Just as below.

let player = AVPlayer(url:Video_URL)
let playerController = LandscapePlayer()
playerController.player = player
present(playerController, animated: true) {
   player.play()
}

Upvotes: 2

Scriptable
Scriptable

Reputation: 19750

If you are using the AVPlayerViewController to present the video you should be able to use the inherited supported iterface orientations property of the view controller, Like so:

let vc = AVPlayerViewController()
vc.supportedInterfaceOrientations = .landscapeRight
// other configs and code to present VC

I haven't had chance to test this but it should work, give it a try and if your having any issues please post the code you have and I'll have a look and update the answer.

EDIT: Taytee pointed out that this is a read only property.

You should be be able to implement it by extending the AVPlayerController and overriding the supported orientation properties, Create this class in a seperate file:

import AVKit

class LandscapeAVPlayerController: AVPlayerViewController {

    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .landscapeLeft
    }
}

and then in your code above, you'd change the class used like

var playerViewController = LandscapeAVPlayerController()

NOTE 1: In your code above you are creating TWO instances of AVPlayerController, you dont need the second one.

NOTE 2: Alot of the config related methods you would normally override in Swift are now properties in Swift 3, so instead of overriding the method, you override the 'getter' for the property

Upvotes: 23

Related Questions