Keywords: iOS | View Controller Orientation | UIDevice
Abstract: This article delves into effective methods for forcing view controller orientation in iOS 8 and above. By analyzing the limitations of traditional approaches, it focuses on solutions using UIDevice's setValue:forKey: method and UINavigationController's attemptRotationToDeviceOrientation method. It explains extension methods for handling orientation control in UINavigationController and UITabBarController, providing complete Objective-C and Swift code examples to help developers achieve precise orientation locking.
Introduction
In iOS development, controlling view controller orientation is a common yet complex task. Prior to iOS 8, developers often forced orientation rotation by setting the status bar orientation and presenting and immediately dismissing a modal view. However, with the release of iOS 8, this method became unreliable and is even discouraged by official guidelines. This article aims to provide a robust solution for forcing view controller orientation in iOS 8 and above.
Limitations of Traditional Methods
In iOS 7 and earlier, developers commonly used the following code snippet to force app orientation:
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:YES];
UIViewController *c = [[UIViewController alloc] init];
[self presentViewController:vc animated:NO completion:nil];
[self dismissViewControllerAnimated:NO completion:nil];This approach relies on setting the status bar orientation and triggering rotation through modal view presentation and dismissal. However, in iOS 8, this method often fails due to stricter system restrictions on orientation control. Moreover, from iOS 8 onward, Apple advises developers to avoid directly manipulating status bar orientation, as it may lead to unpredictable behavior or app rejection.
Solutions for iOS 8 and Above
For iOS 8 and above, a widely accepted solution involves using the setValue:forKey: method of UIDevice in conjunction with the attemptRotationToDeviceOrientation method of UINavigationController. The core idea is to directly set the device orientation and notify the system to rotate.
Objective-C Implementation
In Objective-C, call the following code in the viewDidAppear: method of the target view controller:
[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeLeft) forKey:@"orientation"];
[UINavigationController attemptRotationToDeviceOrientation];Here, UIInterfaceOrientationLandscapeLeft can be replaced with other orientation constants as needed, such as UIInterfaceOrientationPortrait or UIInterfaceOrientationLandscapeRight. The first line sets the device orientation via key-value coding, while the second line triggers the system to re-evaluate the current orientation and perform rotation animations.
Swift Implementation
In Swift 3 and later, the equivalent code is:
let value = UIInterfaceOrientation.landscapeLeft.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
UINavigationController.attemptRotationToDeviceOrientation()Similarly, UIInterfaceOrientation.landscapeLeft can be replaced with other enumeration values. This method works from iOS 7 to iOS 10, but may require additional considerations in iOS 11 and above, as Apple has further tightened orientation control APIs.
Handling Container View Controllers
When view controllers are embedded in UINavigationController or UITabBarController, orientation control becomes more complex. By default, these container controllers override the orientation settings of child controllers. To address this, extensions can be added to delegate orientation decisions to the currently visible view controller.
UINavigationController Extension
In Swift, add an extension to UINavigationController:
extension UINavigationController {
open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return visibleViewController?.supportedInterfaceOrientations ?? super.supportedInterfaceOrientations
}
open override var shouldAutorotate: Bool {
return visibleViewController?.shouldAutorotate ?? super.shouldAutorotate
}
}This extension ensures that the navigation controller uses the orientation settings of the current visible view controller, allowing child controllers to independently control orientation.
UITabBarController Extension
Similarly, add an extension to UITabBarController:
extension UITabBarController {
open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if let selected = selectedViewController {
return selected.supportedInterfaceOrientations
}
return super.supportedInterfaceOrientations
}
open override var shouldAutorotate: Bool {
if let selected = selectedViewController {
return selected.shouldAutorotate
}
return super.shouldAutorotate
}
}This extension ensures that the tab bar controller uses the orientation settings of the currently selected view controller, resolving orientation issues in complex view hierarchies.
Implementing Orientation Locking
After setting up extensions for container controllers, orientation locking can be implemented in individual view controllers. For example, to lock to landscape orientation, override the supportedInterfaceOrientations method:
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .landscapeRight
}To disable auto-rotation, override the shouldAutorotate method:
override var shouldAutorotate: Bool {
return false
}Combined with the previous extensions and orientation setting code, this enables precise orientation control, ensuring consistent behavior across different devices.
Considerations and Best Practices
When using the above methods, note the following: First, ensure that orientation setting code is called in viewDidAppear: to avoid triggering rotation before the view is fully loaded. Second, for universal apps, orientation settings may need adjustment based on device type (e.g., iPhone or iPad), which can be achieved by checking UIDevice.current.userInterfaceIdiom. Additionally, while this method is effective in most cases, it may require adjustments in edge cases or future iOS versions, so regular testing and monitoring of Apple's official documentation updates are recommended.
Conclusion
In iOS 8 and above, the best practice for forcing view controller orientation is to use the setValue:forKey: method of UIDevice and the attemptRotationToDeviceOrientation method of UINavigationController, combined with extensions for container controllers to ensure proper delegation of orientation decisions. This approach avoids the limitations of traditional methods and provides a more reliable and maintainable solution. With the code examples and detailed explanations provided in this article, developers can easily implement complex app orientation requirements, enhancing user experience.