Search code examples
iosiphoneswift3uiinterfaceorientationfeasibility

Programmatically lock iDevice orientation


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.


Solution

  • 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()
        }
    
    }