Disable rotation for View Controller in Navigation Controller
Asked Answered
R

7

6

First of all, this isn't a duplicate. I've looked at all of the questions related to this on SO and none of them work for me. Hopefully it's just because I'm new to iOS development but I suspect this isn't possible in my case. I've attached a picture with the view controller circled that I want to disable rotation for.

I've already tried: Subclassing the view controller that I want to disable rotation for and using the shouldAutoRotate method by setting it to NO. Apparently this doesn't work because it's the navigation controller that dictates whether its view controllers can rotate. So, I subclassed UINavigationController and used this class in storyboard instead of the default navigation controller. In this class I've set shouldAutoRotate to NO. It still doesn't work. Don't really know what I'm doing wrong.

When I extend the root view controller with my view controller class with shouldAutoRotate set to NO, it disables rotation...for the whole app. This is not what I want. I only want the rotation to be disabled for the view controller circled in the picture.

enter image description here

Thanks in advance!

Rath answered 19/1, 2014 at 12:12 Comment(1)
this was answered by many time in different solution but the answer is same for your situation also. Check for my answer for more details.Cowardly
A
12

Add your AppDelegate.h

@property (nonatomic , assign) bool blockRotation;

AppDelegate.m

-(NSUInteger)application:(UIApplication *)application       supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if (self.blockRotation) {
    return UIInterfaceOrientationMaskPortrait;
}
return UIInterfaceOrientationMaskAll;
}

In view Controller that you want to disable rotation

- (void)viewDidLoad
{
 [super viewDidLoad];
    AppDelegate* shared=[UIApplication sharedApplication].delegate;
    shared.blockRotation=YES;
}

-(void)viewWillDisappear:(BOOL)animated{
  AppDelegate* shared=[UIApplication sharedApplication].delegate;
  shared.blockRotation=NO;

  }

Swift 4.2 and later:

In your AppDelegate.swift:

var blockRotation = false

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    if blockRotation {
        return .portrait
    }
    return .all
}

In your viewController:

override func viewDidLoad() {
    super.viewDidLoad()
    AppDelegate.shared.blockRotation = true
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    AppDelegate.shared.blockRotation = false
}
Alastair answered 18/10, 2014 at 14:31 Comment(2)
Even if it looks like a hack, it solves the problem for me. Only when you have turned the device when another ViewController was visible, the ViewController is staill turned. Just a remark: in your AppDelegate you called it blockRotation but in the ViewController allowRotation.Indult
Small remark for those who this work is not working on iPad/iOS 10, in project settings set Requires full screen to YES.Granthem
T
6

Thanks to @ozgur for the fix which worked for me. I would 'vote up', but apparently I'm not good enough (whatever!) to vote whether a fix works or not. Anyway, here it is in Swift:

In AppDelegate.swift:

class AppDelegate: UIResponder, UIApplicationDelegate {


var blockRotation: Bool = false

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int {

    if (self.blockRotation) {
        println("supportedInterfaceOrientations - PORTRAIT")
        return Int(UIInterfaceOrientationMask.Portrait.rawValue)
    } else {
        println("supportedInterfaceOrientations - ALL")
        return Int(UIInterfaceOrientationMask.All.rawValue)
    }
}

In the ViewController that you want to block rotation, add UIApplicationDelegate to your class...

class LoginViewController: UIViewController, UITextFieldDelegate, UIApplicationDelegate {

and then create a reference to the AppDelegate...

var appDelegate = UIApplication.sharedApplication().delegate as AppDelegate

In viewDidLoad, set appDelegate.blockRotation = true:

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.

    appDelegate.blockRotation = true

}

In viewWillAppear, set the orientation to force the device to the chosen orientation (Portrait in this example):

override func viewWillAppear(animated: Bool) {

    let value = UIInterfaceOrientation.Portrait.rawValue
    UIDevice.currentDevice().setValue(value, forKey: "orientation")

}

Then in viewWillDisappear, or in prepareForSegue, set appDelegate.blockRotation = false:

override func viewWillDisappear(animated: Bool) {
    appDelegate.blockRotation = false
}

This worked for me in this exact (multiple view controllers in a Navigation Controller) scenario, after many hours of reading other solutions on this site. Thanks again to @ozgur - I'd up-vote your answer if I could!

Happy trails!

Technicality answered 17/4, 2015 at 11:8 Comment(4)
Hi there. Great answer! Your answer is the only one that has helped me. I was however wondering about one thing. In the simulator the contents are not automatically roated once the blockRotation is changed, it still requires the virtual device to be changed to landscape before it locks into its proper orientation. Do you know how I can force the contents to change to landscape? Thanks!Nervous
Hi @Stew - E. Many thanks. It took me HOURS to suss this out, so I'm glad it has saved you some time also. With regards to forcing the rotation, I have not tried this, but you could try the following in viedDidAppear: let value = UIInterfaceOrientation.LandscapeLeft.rawValue UIDevice.currentDevice().setValue(value, forKey: "orientation")Technicality
Works in viewWillAppear too.Technicality
You don't need to implement UIApplicationDelegate or anything. Just use (UIApplication.sharedApplication().delegate as! AppDelegate). blockRotation = trueFourdrinier
W
3

You have to check in your root view controller is rotation allowed in current top controller and return YES or NO in supportedInterfaceOrientations method. So it should be like the following code in your root controller (adapt the code to your case):

- (NSUInteger)supportedInterfaceOrientations
{    
    return [self.navigationController.topViewController supportedInterfaceOrientations];
}

Then in each view controller add supported interface orientations, for example:

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}
Wiencke answered 19/1, 2014 at 12:27 Comment(10)
When I put that code in my root view controller, it disables rotation for every view controller even if I put supportedInterfaceOrientations in the code. Any ideas?Rath
You goal is to delegate decision of the supported rotation to particular controller. I believe that your code inside supportedInterfaceOrientation should like the following: return [<selected controller in menu>.navigationController.topViewController supportedInterfaceOrientations];Wiencke
Ok, In my root view controller I have - (NSUInteger)supportedInterfaceOrientations { return [self.navigationController.topViewController supportedInterfaceOrientations]; } And in the DashboardViewController I have - - (NSUInteger)supportedInterfaceOrientations { return UIInterfaceOrientationMaskLandscape; } The thing is, even when this method in the view controller and it is getting called, it's still stuck in portrait even though I say UIInterfaceOrientationMaskLandscape;Rath
I do not know exactly the hierarchy of VC in you project, so can you tell me how can you access current visible DashboardViewController inside root view controller?Wiencke
Ok. As you can see in the picture, I have a Slide Menu Root View Controller. This then goes to the menu view controller and from there, each of the menu items has a navigation controller. So each of the separate menu options has its own navigation controller. I use return self.navigationController.topViewController.Rath
Did you add menu vc as child to root vc, or you pushed it into navigation controller of the root vc?Wiencke
I did it in storyboard, it's a slide menu left menu segue from Slide Menu Root View Controller to Menu View ControllerRath
The segue is custom, so you somehow handle this custom segue in your code :)Wiencke
Is that the problem? Don't really understand why all of this doesn't work.Rath
This helped me. I don't understand why the UINavigationController doesn't defer to its top VC by default. I added these methods to my custom NC class, delegating both to the topViewController, and bingo. I'd recommend implementing preferredInterfaceOrientationForPresentation too in any VCs with limited orientation support.Renaterenato
D
3

For those of you using Swift 2, you can follow Andre's answer, but in step one, use the code below in your AppDelegate.swift:

class AppDelegate: UIResponder, UIApplicationDelegate {


var blockRotation: Bool = false

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

    if (self.blockRotation) {
        print("supportedInterfaceOrientations - PORTRAIT")
        return UIInterfaceOrientationMask.Portrait
    } else {
        print("supportedInterfaceOrientations - ALL")
        return UIInterfaceOrientationMask.All
    }
}

supportedInterfaceOrientationsForWindow: now returns a UIInterfaceOrientationMask instead of an Int.

Debbidebbie answered 23/9, 2015 at 17:36 Comment(0)
V
1

My case has 3 view controller:
- first view controller: portrait
- second view controller: landscape right (has navigation controller and was presented by first view controller)
- third view controller: portrait (has navigation controller and was pushed by second view controller )
And this is my solution in swift 3:
------------------------------------
At AppDelegate:
- Add this property to save your setting

private var orientation: UIInterfaceOrientationMask = .portrait

-Then create a function to set rotate for your device:

func rotateScreen(orientation: UIInterfaceOrientationMask) {
    self.orientation = orientation
    var value = 0;
    if orientation == .landscapeRight {
        value = UIInterfaceOrientation.landscapeRight.rawValue
    }else {
        value = UIInterfaceOrientation.portrait.rawValue
    }
    UIDevice.current.setValue(value, forKey: "orientation")
}

- Finally, implement support orientation method:

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    return self.orientation
}

--------------------------------
Then you can call like this before display destination view controller

(UIApplication.shared.delegate as! AppDelegate).rotateScreen(orientation: .landscapeRight)

For example, in my case: When user tap on button at the First to present the Second, I'll do like this

(UIApplication.shared.delegate as! AppDelegate).rotateScreen(orientation: .landscapeRight)
self.present(navigation, animated: true, completion: nil)

And the close button at the Second I'll do like this

(UIApplication.shared.delegate as! AppDelegate).rotateScreen(orientation: .portrait)
self.dismiss(animated: true, completion: nil)

That's all! You could never mind using navigation controller or not. Hope this helps you guys ^__^!

Vivyanne answered 22/9, 2017 at 4:56 Comment(0)
C
0

UINavigationController+Rotation Category

When u usign navigation controller SupportedInterfaceOrientaions of ViewController will not work adding this category to ur Project will get the response from your viewController also instead of NavigationController alone.

Cowardly answered 19/1, 2014 at 13:25 Comment(1)
This doesn't work. This isn't the same situation as you're describing which is why it doesn't work.Rath
P
0

In my case I have the view controller embedded in a navigation controller, and most of the solutions don't work because the viewcontroller depends on the navigation controller orientation. For this when you create the instance of the view controller you have to cast it to UINavigationController :

    let theViewController = UIViewController()
    if let navController = theViewController as? UINavigationController {
        navController.delegate = self
    }

And then add this extension: extension PlaySplashViewController: UINavigationControllerDelegate {

func navigationControllerSupportedInterfaceOrientations(_ navigationController: UINavigationController) -> UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.portrait
}

}

In this case this going to set portrait orientation for the navigation controller so the view controller going to use this orientation too.

Pagel answered 21/10, 2019 at 16:27 Comment(1)
You can't cast a UIViewController to a UINavigationController like that. This will always fail.Forrester

© 2022 - 2024 — McMap. All rights reserved.