31

I'm trying to prevent rotation on one UIViewController and I can't achieve that.

I'm doing something like this:

open override var shouldAutorotate: Bool {
    get {
        return false
    }
}

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    get {
        return .portrait
    }
}

And the UIViewControler stills rotating. The UIViewController is inside a UINavigationController opened modally.

I have looked a lot of questions from here and none answers works for me.

In Swift 2 I used to override shouldAutorotate but in Swift 3 that function doesn't exist anymore.

How can I do that in Swift 3 what I used to do in Swift 2?

pableiros
  • 14,932
  • 12
  • 99
  • 105
  • 1
    What does your `supportedInterfaceOrientations` look like? – matt Nov 04 '16 at 00:51
  • @matt I edited the question adding that – pableiros Nov 04 '16 at 00:54
  • Thanks. Okay, so if this is a presented view controller, it should appear only in portrait and stay there. If that's not happening, there must be more to the story. Can you describe _how_ this view controller is presented? Prove to me that it is a fullscreen top-level presented view controller. – matt Nov 04 '16 at 00:56
  • @matt The view controller is presented modally via a bar button item tapped and the view controller is inside a navigation controller, I don't know if is not possible to prevent rotation on a modal view controller – pableiros Nov 04 '16 at 00:59
  • 1
    If you present a view controller that itself is inside a navigation controller, that is not a presented view controller. The navigation controller is the presented view controller. So the problem would be that your view controller's settings here are irrelevant; it is the _navigation controller_ that is the presented view controller, so _it_ gets to determine the rotation (if I understand you correctly). – matt Nov 04 '16 at 01:08
  • `shouldAutorotate` is still there, but instead of a function, it is now a `var`. And what you have in your code seems correct. I think Matt's suggestion is what you need ... – Yuchen Nov 04 '16 at 01:15
  • @matt thanks for the advise, I create a custom class for the NavigationController and I applied the functions I described from my question and it works – pableiros Nov 04 '16 at 15:38

6 Answers6

45

I don't know why is a vote to close the question if I can reproduce this behavior a lots of times. The UIViewController is inside a UINavigationController opened modally.

This is what I did to solve the problem.

I create this class and I set to the UINavigationController that containt the UIViewController that I want to prevent to rotate

class NavigationController: UINavigationController { 

    override var shouldAutorotate: Bool {
        return false
    }

    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .portrait
    }

}

And thats it, it works for me

Kyle Clegg
  • 38,547
  • 26
  • 130
  • 141
pableiros
  • 14,932
  • 12
  • 99
  • 105
  • but what if you only want a specific UIViewController not to rotate and not the entire navigation stack? This answer seems like a better one: http://stackoverflow.com/a/39805665 – Roy Shilkrot Apr 20 '17 at 14:16
  • @RoyShilkrot It is ok, the goal of the question was how to prevent the rotation of one view controller inside a navigation controller, not how to prevent a specific view controller inside a entire navigation stack – pableiros Apr 20 '17 at 14:48
  • 1
    Works nicely in conjunction with @Thuggish Nuggets observation below - so two upvotes – Clay Aug 09 '18 at 19:52
  • This did not work until checking `Requires full screen` in the `General` tab of the app. With that, the Thuggish Nuggets code above should not be necessary. – C6Silver Jan 31 '19 at 07:46
  • amazing great ! – Marin Feb 03 '19 at 00:39
11

Add this code to AppDelegate.swift

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

struct AppUtility {
    static func lockOrientation(_ orientation: UIInterfaceOrientationMask) {
        if let delegate = UIApplication.shared.delegate as? AppDelegate {
            delegate.orientationLock = orientation
        }
    }

    static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) {
        self.lockOrientation(orientation)
        UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
    }
}

Add to the viewcontroller that you want to force an orientation:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    //let value = UIInterfaceOrientation.landscapeLeft.rawValue
    //UIDevice.current.setValue(value, forKey: "orientation")


    AppDelegate.AppUtility.lockOrientation(.landscapeLeft)

}
  • This works for me, but the accepted answer is much cleaner and also works. Is there a specific scenario where the accepted answer is not possible and only this works? – paul Oct 18 '17 at 13:47
  • This works for me; the accepted answer wasn't working. – Moondra Aug 10 '18 at 04:38
  • This works great for me; the best solution, the accepted answer wasn't working very well. – Luca Iaconelli May 14 '19 at 08:35
  • Perfect answer! – jegadeesh Nov 26 '19 at 10:29
  • This works, but do you find the previous view controller will deinit() ? – Tieda Wei Apr 17 '20 at 21:28
  • While this solution might work, it uses a private API, which means it may break in future versions; plus, you may have problems with the App Store review guidelines. – Vym Nov 16 '21 at 16:05
  • As stated by @Vym, this method was prone to break in newer versions: in fact it did in 16.0. Using this solution now will fail. – David Apr 18 '23 at 03:08
4

For me, the voted answer didn't work. Instead,

override open var shouldAutorotate: Bool {
    return false
}

override open var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
    return UIApplication.shared.statusBarOrientation
}

Those codes work. Confirmed in Swift 4.

14c
  • 111
  • 7
3

I found that iOS 11 wasn't calling these methods at all unless I had implemented the following UIApplicationDelegate method in my AppDelegate class:

application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?)
Beltalowda
  • 4,558
  • 2
  • 25
  • 33
0

iOS 11, Swift 5

The new secret sauce is to add this in viewDidLoad()

UIDevice.current.setValue(self.preferredInterfaceOrientationForPresentation.rawValue, forKey: "orientation")

Also be sure to override preferredInterfaceOrientationForPresentation to return your preferred orientation:

override var preferredInterfaceOrientationForPresentation : UIInterfaceOrientation { return UIInterfaceOrientation.portrait }

Found the answer here: https://nunoalexandre.com/2018/08/24/forcing-an-orientation-in-ios

bgolson
  • 3,460
  • 5
  • 24
  • 41
  • This method utilised a private API and was removed in iOS 16.0. Using this solution now will fail. – David Apr 18 '23 at 03:09
0

Swift 5 Answer - this is how you do it now in 2022

@objcMembers class YourViewController: UIViewController
{
    override var shouldAutorotate: Bool { return true }
    ...
    init() {}
    ...
 }
Cliff Ribaudo
  • 8,932
  • 2
  • 55
  • 78