2

In an app we are developing we have an option to let the user choose preferred orientation (i.e if they choose Portrait the app will be locked to portrait orientation and the same for Landscape and if Both is opted the app will work on all orientation) I am sharing the code for what I have tried, and I'm not sure whether this functionality is feasible at all.

//MARK:- ORIENTATION
func changeOrientation(orientation: String) {
    switch orientation {
    case "Portrait":
        UserDefaults.standard.set("Portrait", forKey: UserDefaultsKeys.preferredOrientation)
        appDelegate.preferredOrientation = "Portrait"
        let value = UIInterfaceOrientation.portrait.rawValue
        UIDevice.current.setValue(value, forKey: "orientation")
        break
    case "Landscape":
        UserDefaults.standard.set("Landscape", forKey: UserDefaultsKeys.preferredOrientation)
        appDelegate.preferredOrientation = "Landscape"
        let value = UIInterfaceOrientation.landscapeLeft.rawValue
        UIDevice.current.setValue(value, forKey: "orientation")
        break
    default:
        UserDefaults.standard.set("Both", forKey: UserDefaultsKeys.preferredOrientation)
        appDelegate.preferredOrientation = "Both"
        break
    }
    /*not necessary*/
    let vc = UIViewController()
    UIViewController.attemptRotationToDeviceOrientation()//forces to rotate
    /*not necessary*/
    self.present(vc, animated: false, completion: nil)
    UIView.animate(withDuration: 0.3, animations: {
        vc.dismiss(animated: false, completion: nil)
    })
    /*not necessary*/
}

open override var supportedInterfaceOrientations: UIInterfaceOrientationMask{
    get {
        switch appDelegate.preferredOrientation {
        case "Portrait":
            return .portrait
        case "Landscape":
            return .landscape
        default:
            return .all
        }
    }
}

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

However if I choose 'Landscape' when in portrait mode, it automatically switches to landscape. But, if I rotate the device back to portrait it is working as well (which shouldn't be working as per the requirement). The requirement is similar to what happens when we setup project only with Portrait mode and how it will behave when the device is rotated to landscape mode.

Nina
  • 1,579
  • 2
  • 21
  • 51
  • I think the approach is wrong here, you need to manage the orientation support in the individual view controllers always, and the presenter or controller, which manages the navigation stack, must inherit the orientation support from the top-most visible visible controller every time. – holex Oct 17 '16 at 15:10

1 Answers1

3

I just wrote a sample project to test if this is possible. Well, I think it is! Unfortunately

UIViewController.attemptRotationToDeviceOrientation()

does not do the job magically how I hope so - it is a little bit more complex than that. Please take a loot at the following code. All the magic you need is happening in the action forceChangeOrientations.

    class ViewController: UIViewController {

    enum Orientation {
        case Landscape
        case Portrait

        mutating func changeOrientation() {
            if self == .Portrait {
                self = .Landscape
            }
            else {
                self = .Portrait
            }
        }

        func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
            switch self {
            case .Landscape:
                return .Landscape
            case .Portrait:
                return .Portrait
            }
        }

        func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
            switch self {
            case .Landscape:
                return UIInterfaceOrientation.LandscapeLeft
            case .Portrait:
                return .Portrait
            }
        }
    }

    var currentOrientation: Orientation = .Portrait

    //    Returns a Boolean value indicating whether the view controller's contents should auto rotate.
    override func shouldAutorotate() -> Bool {
        return true
    }

//    Returns all of the interface orientations that the view controller supports.
    override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return currentOrientation.supportedInterfaceOrientations() //UIInterfaceOrientationMask.All
    }

//    Returns the interface orientation to use when presenting the view controller.
    override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
        return UIInterfaceOrientation.Portrait
    }

    @IBAction func forceChangeOrientations(sender: AnyObject) {
        self.currentOrientation.changeOrientation()

        let value = self.currentOrientation.preferredInterfaceOrientationForPresentation().rawValue
        UIDevice.currentDevice().setValue(value, forKey: "orientation")
        UIViewController.attemptRotationToDeviceOrientation()
    }

}
dvp.petrov
  • 1,110
  • 13
  • 20
  • Thanks for the answer. I tried your answer in separate project and it works as expected :) Just a question, if I set `currentOrientation` as global variable, will it work on all classes? so that all the pages will stay on selected orientation. – Nina Oct 18 '16 at 06:43
  • Yes you can. :) Please have in mind that if you (and you probably will) use navigationControllers in your application, you should subclass navigationController and add those method to it, so you have this behaviour for all controllers in its navigationStack. If you need further help, I could upload a test project somewhere, so you can take a look at more complex navigationController situation. – dvp.petrov Oct 18 '16 at 08:32
  • Can you please upload a sample project for Navigation controller? It will be very helpful :) – Nina Oct 18 '16 at 09:47
  • Does this link work for you? https://dvppetrov@bitbucket.org/dvppublic/rotationlocks.git – dvp.petrov Oct 18 '16 at 11:22
  • Got it. Thank you very much for your time, appreciate it :) – Nina Oct 18 '16 at 11:56