1

Basically, here's my page structure, where:

TBC = UITabBarController; VC = UIViewController; NVC = UINavigationViewController

                  +--> [NVC] --> [VC] --> ... --> [VC]
 Home             |
 [VC] --> [TBC] --+--> [NVC] --> [VC] --> ... --> [VC]
                  |
                  +--> [NVC] --> [VC] --> ... --> [VC]

and I'd like some specific VCs to be allowed to view on different orientations. Most likely, these would be the leaf node VCs. It might be a page for displaying a picture or something.

Most of the app should only be viewed in portrait orientation. Think of the Facebook app, where most of the core pages are viewed in portrait and pictures/videos can be viewed in either portrait or landscape.

ton
  • 1,143
  • 3
  • 13
  • 34

2 Answers2

3

I have based my solution from this answer.

In AppDelegate.swift:

func application (application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
  return checkOrientation(self.window?.rootViewController)
}

checkOrientation function:

func checkOrientation (viewController: UIViewController?) -> UIInterfaceOrientationMask {
  if viewController == nil {
    return UIInterfaceOrientationMask.Portrait

  } else if viewController is MyViewController {
    return UIInterfaceOrientationMask.AllButUpsideDown

  } else if viewController is MyTabBarController {
    if let tabBarController = viewController as? MyTabBarController,
           navigationViewControllers = tabBarController.viewControllers as? [MyNavigationController] {
      return checkOrientation(navigationViewControllers[tabBarController.selectedIndex].visibleViewController)
    } else {
      return UIInterfaceOrientationMask.Portrait
    }

  } else {
    return checkOrientation(viewController!.presentedViewController)
  }
}

Logic with if/else and recursion could be improved, but it works for now.

If the previous/next UIViewControllers have to be forced with an orientation, you should add this line to the current UIViewController's viewWillDisappear method. This will force the previous/next views

override func viewWillDisappear (animated: Bool) {
  super.viewWillDisappear(animated)
  UIDevice.currentDevice().setValue(UIInterfaceOrientation.Portrait.rawValue, forKey: "orientation")
}
Community
  • 1
  • 1
ton
  • 1,143
  • 3
  • 13
  • 34
  • 1
    I having an issue where the "else if viewController is MyViewController {" is never executed, I verified that I replaced MyViewController with the right view controller name but I think that my issue has to do with my view controller being a TableViewController. Could you offer a suggestion on how to fix this? Thank you – Jace Nov 04 '16 at 09:17
1

Things can get quite messy when you have a complicated view hierarchy, like having multiple navigation controllers and/or tab view controllers.

This implementation puts it on the individual view controllers to set when they would like to lock orientations, instead of relying on the App Delegate to find them.

Swift 3

In AppDelegate:

/// set orientations you want to be allowed in this property by default
var orientationLock = UIInterfaceOrientationMask.all

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

In some other global struct or helper class, here I created AppUtility:

struct AppUtility {

    static func lockOrientation(_ orientation: UIInterfaceOrientationMask) {

        if let delegate = UIApplication.shared.delegate as? AppDelegate {
            delegate.orientationLock = orientation
        }
    }

    /// OPTIONAL Added method to adjust lock and rotate to the desired orientation
    static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) {

        self.lockOrientation(orientation)

        UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
    }

}

Then in the desired ViewController you want to lock orientations:

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

    AppUtility.lockOrientation(.portrait)
    // Or to rotate and lock
    // AppUtility.lockOrientation(.portrait, andRotateTo: .portrait)

}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    // Don't forget to reset when view is being removed
    AppUtility.lockOrientation(.all)
}
bmjohns
  • 6,344
  • 1
  • 33
  • 39