Changing orientations programmatically does not work fine - ios

My whole application is in portrait mode. I just want to use one view controller in landscape mode (left).
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask
{
if let _navigationController = window?.rootViewController as? UINavigationController {
if _navigationController.topViewController is FullScreenPlayerVC {
return UIInterfaceOrientationMask.LandscapeLeft
}
}
return UIInterfaceOrientationMask.Portrait
}
This is my controller A
override func shouldAutorotate() -> Bool
{
return true
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask
{
return UIInterfaceOrientationMask.Portrait
}
Now i push Controller B. This is my controller B
override func viewDidAppear(animated: Bool)
{
super.viewDidAppear(animated)
let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
}
override func shouldAutorotate() -> Bool
{
return true
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask
{
return UIInterfaceOrientationMask.Landscape
}
It works as per my requirement when i push controller B while holding my device in portrait mode but if i am holding my phone in landscape left already.
It does not perform desired action. Searched a lot about it but not able to find the solution yet.
I have tried many solutions but nothings working fine.

This is a generic solution for your problem and others related.
1. Create auxiliar class UIHelper and put on the following methods:
/**This method returns top view controller in application */
class func topViewController() -> UIViewController?
{
let helper = UIHelper()
return helper.topViewControllerWithRootViewController(rootViewController: UIApplication.shared.keyWindow?.rootViewController)
}
/**This is a recursive method to select the top View Controller in a app, either with TabBarController or not */
private func topViewControllerWithRootViewController(rootViewController:UIViewController?) -> UIViewController?
{
if(rootViewController != nil)
{
// UITabBarController
if let tabBarController = rootViewController as? UITabBarController,
let selectedViewController = tabBarController.selectedViewController {
return self.topViewControllerWithRootViewController(rootViewController: selectedViewController)
}
// UINavigationController
if let navigationController = rootViewController as? UINavigationController ,let visibleViewController = navigationController.visibleViewController {
return self.topViewControllerWithRootViewController(rootViewController: visibleViewController)
}
if ((rootViewController!.presentedViewController) != nil) {
let presentedViewController = rootViewController!.presentedViewController;
return self.topViewControllerWithRootViewController(rootViewController: presentedViewController!);
}else
{
return rootViewController
}
}
return nil
}
2. Create a Protocol with your desire behavior, for your specific case will be landscape.
protocol orientationIsOnlyLandscape {}
Nota: If you want, add it in the top of UIHelper Class.
3. Extend your View Controller
In your case:
class B_ViewController: UIViewController,orientationIsOnlyLandscape {
....
}
4. In app delegate class add this method:
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
let presentedViewController = UIHelper.topViewController()
if presentedViewController is orientationIsOnlyLandscape {
return .landscape
}
return .portrait
}
Final Notes:
If you that more class are in landscape mode, just extend that
protocol.
If you want others behaviors from view controllers, create other protocols and follow the same structure.
This example solves the problem with orientations changes after push
view controllers

Related

Landscape view controller presented from portrait view controller occasionally appears in portrait orientation with landscape bounds

I have a simple app in which a portrait UINavigationController presents a landscape UINavigationController. Usually it works fine. Sometimes though, the landscape view controller appears in portrait orientation, but with landscape bounds.
Here's how it works: to restrict the orientation of the navigation controllers, I have a class, OrientableNavigationController that derives from UINavigationController and exposes the rotation-specific properties:
class OrientableNavigationController: UINavigationController {
private var _supportedInterfaceOrientations: UIInterfaceOrientationMask = .all
private var _shouldAutorotate: Bool = false
private var _preferredInterfaceOrientationForPresentation: UIInterfaceOrientation = .portrait
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
get { return _supportedInterfaceOrientations }
set { _supportedInterfaceOrientations = newValue }
}
override var shouldAutorotate: Bool {
get { return _shouldAutorotate }
set { _shouldAutorotate = newValue }
}
override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
get { return _preferredInterfaceOrientationForPresentation }
set { _preferredInterfaceOrientationForPresentation = newValue }
}
}
I init one of these within the AppDelegate, give it a view controller and set it as the rootViewController of the window:
private var portraitNavigationController = OrientableNavigationController()
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
portraitNavigationController.supportedInterfaceOrientations = .portrait
portraitNavigationController.shouldAutorotate = true
portraitNavigationController.preferredInterfaceOrientationForPresentation = .portrait
let portraitViewController = PortraitViewController()
portraitViewController.delegate = self
portraitNavigationController.pushViewController(portraitViewController, animated: false)
window = UIWindow(frame: UIScreen.main.bounds)
window!.rootViewController = portraitNavigationController
window!.makeKeyAndVisible()
return true
}
It looks like this:
And that 'Present Landscape View Controller' button is hooked up to do this:
func portraitViewControllerButtonWasTouched(_ viewController: PortraitViewController) {
let landscapeNavigationController = OrientableNavigationController()
landscapeNavigationController.supportedInterfaceOrientations = .landscape
landscapeNavigationController.shouldAutorotate = true
landscapeNavigationController.preferredInterfaceOrientationForPresentation = .landscapeLeft
landscapeNavigationController.isNavigationBarHidden = true
let landscapeViewController = LandscapeViewController()
landscapeNavigationController.pushViewController(landscapeViewController, animated: false)
portraitNavigationController.present(landscapeNavigationController, animated: true)
}
Most of the time, this works fine. The landscape view controller is presented like so:
But sometimes, this happens:
This occurs in both simulator and device. Any ideas?

How to lock orientation for iPhone for certain view controllers - Swift?

I have 2 View Controllers, VC1 and VC2.
VC1 currently presents VC2 modally.
VC1 only orientation should be portrait but VC2 can have all orientations.
The issue is when I am in VC2 and I rotate to landscape mode and then dismiss, VC1 is in landscape mode also! That should never happen!
NOTE: There is no Navigation Controller or UITabbarcontroller
I am adding my code bellow.
Appdelagate:
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
if let rootViewController = self.topViewControllerWithRootViewController(rootViewController: window?.rootViewController) {
if (rootViewController.responds(to: Selector(("canRotate")))) {
// Unlock landscape view orientations for this view controller
return .allButUpsideDown
}
}
// Only allow portrait (standard behaviour)
return .portrait;
}
private func topViewControllerWithRootViewController(rootViewController: UIViewController!) -> UIViewController? {
if (rootViewController == nil) { return nil }
if (rootViewController.isKind(of: (UITabBarController).self)) {
return topViewControllerWithRootViewController(rootViewController: (rootViewController as! UITabBarController).selectedViewController)
} else if (rootViewController.isKind(of:(UINavigationController).self)) {
return topViewControllerWithRootViewController(rootViewController: (rootViewController as! UINavigationController).visibleViewController)
} else if (rootViewController.presentedViewController != nil) {
return topViewControllerWithRootViewController(rootViewController: rootViewController.presentedViewController)
}
return rootViewController
}
Code in VC2:
override func viewDidLoad() {
super.viewDidLoad()
UIDevice.current.setValue(Int(UIInterfaceOrientation.portrait.rawValue), forKey: "orientation")
}
func canRotate() -> Void {}
Link to where I went for help and found this code
Website where I found Code
Thanks so much for the help!
You need to follow the below steps to lock rotation for specific ViewControllers :-
Step 1: While creating your project, allow all the orientations. Do not select anything in below image.
Step 2: If you want VC1 to have only Portrait Orientation the implementation then add the below two functions in your ViewController Class
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.all //return the value as per the required orientation
}
override var shouldAutorotate: Bool {
return false
}
Step 3: If you wish VC2 to have all the orientation then do not add any code for it.
So the conclusion:-
In project setting, allow all the orientations for whole project. Restriction should be at ViewControllers level not at project level.
If you wish any VC to have all orientation then don't write any code.
If you wish any VC to have specific orientation then implement two functions above.

AVPlayer in fullscreen doesn't rotate

In our project we have blocked/unblocked the orientations of a view following this post and inserting the code below. Everything works great.
AppDelegate.swift
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
if let rootViewController = self.topViewControllerWithRootViewController(window?.rootViewController) {
if (rootViewController.respondsToSelector(Selector("canRotate"))) {
// Unlock landscape view orientations for this view controller
return .AllButUpsideDown;
}
}
// Only allow portrait (standard behaviour)
return .Portrait;
}
private func topViewControllerWithRootViewController(rootViewController: UIViewController!) -> UIViewController? {
if (rootViewController == nil) { return nil }
if (rootViewController.isKindOfClass(UITabBarController)) {
return topViewControllerWithRootViewController((rootViewController as! UITabBarController).selectedViewController)
} else if (rootViewController.isKindOfClass(UINavigationController)) {
return topViewControllerWithRootViewController((rootViewController as! UINavigationController).visibleViewController)
} else if (rootViewController.presentedViewController != nil) {
return topViewControllerWithRootViewController(rootViewController.presentedViewController)
}
return rootViewController
}
...
ViewController.swift
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillDisappear(animated : Bool) {
super.viewWillDisappear(animated)
if (self.isMovingFromParentViewController()) {
UIDevice.currentDevice().setValue(Int(UIInterfaceOrientation.Portrait.rawValue), forKey: "orientation")
}
}
func canRotate() -> Void {}
}
The app is portrait-only, except a view where we have enable all the orientations. In this view there are also a container with an AVPlayer and the problem is that I don't know how to enable all orientations in this AVPlayerViewController also. The app allows all orientations in the view that contains the player, but once I have put the player in fullscreen this one doesn't rotate. I have tried using the code previously mentioned in the AVPlayerViewController of the AVPlayer in the container, but the player doesn't rotate.

lock orientation of a viewController in swift

I'm a newbie in Swift and i have a problem locking the orientation to portrait in a viewController. Actually i have locked it using this code in my Custom Navigation Controller
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if (self.visibleViewController is ViewController)
{
return UIInterfaceOrientationMask.Portrait
}
return .All
}
Everything works fine and the ViewController is locked to portrait.The problem is when return to this controller from another in landscape mode.
if i return to ViewController (pressing back from the NextViewController) in landscape then the ViewController appeared in landscape. Any suggestion?
In swift 3 this solution works
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
if self.window?.rootViewController?.presentedViewController is LockedViewController {
return UIInterfaceOrientationMask.portrait
} else {
return UIInterfaceOrientationMask.all
}
}
Change LockedViewController to match the controller you would like locked.
You can override the methods below
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.Portrait
}
override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
return UIInterfaceOrientation.Portrait
}
Then, in your viewDidLoad, force the orientation by adding the code below
UIDevice.currentDevice().setValue(UIInterfaceOrientation.Portrait.rawValue, forKey: "orientation")
For swift3.0
To restrict different orientations for different views You need to do following things :
In App delegate File :
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
if self.window?.rootViewController?.presentedViewController is OtherViewController {
return UIInterfaceOrientationMask.All;
} else {
return UIInterfaceOrientationMask.Portrait;
}
}

Disable rotation for item in tabBarController

I have a tabBarController which has 4 items. One of them is a camera (a barcode scanner) which I implemented with AVCaptureSession. So, if you tab the tab "scanner" will automatically show you a camera screen.
The problem is that I can't disable autorotate of individual items of the tabBarController. So, the screen of the camera rotates when you rotate the device and is very weird.
I tried:
override func shouldAutorotate() -> Bool {
return false
}
and
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return .Portrait
}
but nothing works.
In your AppDelegate add the following
var shouldSupportAllOrientation = false
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
if (shouldSupportAllOrientation == true){
return UIInterfaceOrientationMask.All
}
return UIInterfaceOrientationMask.Portrait
}
Then go to each view and add the following in viewWillAppear
let appdelegate = UIApplication.sharedApplication().delegate as! AppDelegate
// false = only portrait
// true = all orientations
appdelegate.shouldSupportAllOrientation = false
Update
To lock the screen when you go from landscape to portrait, just add this code in viewWillAppear.
let value = UIInterfaceOrientation.Portrait.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")

Resources