To enable the rotation of a single view controller used to display the preview of Images/Videos. It is intuitive to allow user to rotate there device and screen changes accordingly, so it feels pleasant. But to achieve this, we need to enable the (almost) all Supported Device orientations.
Ex: `Portrait`, `LandscapeLeft`, `LandscapeRight`.
By enabling Supported Device orientations either from Info.plist or via AppDelegate.
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return .all
}By doing this we are allowing other view controllers to also rotate if device orientation changes. But we want only Image/Video Previewing view controller is allowed to Rotate in all orientations.
- Documentation: Handling View Rotation
A view controller can override the `supportedInterfaceOrientations`
method to limit the list of supported orientations.By doing we can allow our view controllers to limited orientations like below:
class ViewController: UIViewController {
...
override var shouldAutorotate: Bool {
return false
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
...
}class PreviewingViewController: UIViewController {
...
override var shouldAutorotate: Bool {
return false
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .allButUpsideDown
}
...
}The ViewController is now the rootViewController of current window
and our PreviewingViewController embedded in UINavigationController is being presented modally covering the entire screen.
- Result: It worked as desired. π
The ViewController is embedded in UINavigationController, and PreviewingViewController is the same as Configuration 1.
- Result: It's not working now.
ViewControllerscreen is now rotating again as device orientation changes. π
The ViewController is embedded in UITabBarController, and PreviewingViewController is the same as Configuration 1.
- Result: Same as result as in
configuration 2.βΉοΈ
Lets put break-point at supportedInterfaceOrientations on both ViewController and PreviewingViewController.
In Configuration 1: Break-point being hit every time when new orientation is applied on both view-controllers.
In Configuration 2: Break-point hit for first time, but not when device rotated on both view-controllers.
In Configuration 3: same as configuration 2.
A view controller can override the `supportedInterfaceOrientations`
method to limit the list of supported orientations.
Typically, the system calls this method `only` on the
root view controller of the window or
a view controller presented to fill the entire screen;
Yes, this is why Configuration 1 is working properly. In this configuration we have our ViewController as the only rootViewController of our window.
if let window = (UIApplication.shared.delegate as? AppDelegate)?.window {
print(window.rootViewController is ViewController)
}
// Prints true.
And for the PreviewingViewController is being presented covering entire screen, so its supportedInterfaceOrientations property is also called every time device changes to new orientation.
So when the device orientation changes we get called for the appropriate UIInterfaceOrientationMask
####So what's the deal for the Configuration 2?
Yes, now we have UINavigationController as our window's rootViewController.
if let window = (UIApplication.shared.delegate as? AppDelegate)?.window {
print(window.rootViewController is UINavigationController)
}
// Prints true.We need to provide appropriate supportedInterfaceOrientations to our UINavigationController controller in order to get notified in ViewController
Lets extend UINavigationController.
extension UINavigationController {
open override var shouldAutorotate: Bool {
return true
}
open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return topViewController?.supportedInterfaceOrientations ?? .allButUpsideDown
}
}Now we have told navigationController to ask its topViewController to return appropriate supportedInterfaceOrientations
As we run, we get hit at break point every-time when device is rotated to new orientation.
extension UITabBarController {
open override var shouldAutorotate: Bool {
return true
}
open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return selectedViewController?.supportedInterfaceOrientations ?? .allButUpsideDown
}
}As we have told UITabBarController to ask its selectedViewController to return its supportedInterfaceOrientations.
As expected, this is working too, Break-point hit when device is rotated to new orientation. Hurray. π
Question: Why we have to extend UINavigationController or UITabBarController (ContainerViewControllers) to do rotation according to its children?
Further reading the documentation...
child view controllers use the portion of the window provided
for them by their parent view controller and
no longer participate directly in decisions
about what rotations are supported.
This may be the default implementation for container view controllers.