Blog

October 20, 2013
|

Only make one single view controller rotate

With every major project there is one distinct problem I always come across:
How can I maintain a portrait orientation only app and still make one view rotate?

The problem

Usually the view controller in question is an image gallery and from my Stackoverflow and internet research this is a quite common problem among ios devs. However the solutions I have found so far are unsatisfactory.

Why is it a problem anyway?

Well, first of all an ios app needs to supply a key

UISupportedInterfaceOrientations

in its so-called Info.plist to let the device know which orientations are supported by the app. This key can be configured in XCode under Target>YourTarget>General>Deployment Info>Device Orientation.

Supported Interface Orientation

In order to use any of these device orientations in any given view in an app, you need to check it here. Meaning: Even if you need Landscape Left and Landscape Right in only one view controller, you will need to check it here.

However this also means that you will have to disable Landscape Left and Landscape Right for all other views that you do not want to rotate.

You could implement the

- (BOOL)shouldAutorotate { return NO; }

method to return NO for every view that should not rotate. However that seems a lot of code in all view controllers, that you do not want to rotate, instead of implementing some method in the view controller that actually should rotate.

The solution

Note: I find this solution a bit hacky and am more than grateful for any suggestions to improve it.

Apart from the Supported Device Orientations key in the plist file, there is a method in our AppDelegate that can help us to restrict the interface orientation.

- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window

This method is called for every view controller that occurs on screen! Therefore we can use it to restrict the supported interface orientations.

How will we do that?

The idea is fairly simple. Since it is called for every view controller and whenever we try to rotate our device, we just need to find out whether out current topmost (meaning visible) view controller is able to change its orientation. My example suggests using a stub method in every view controller that should rotate. (idea taken from: http://stackoverflow.com/a/13881884/636931) My stub method is called

- (void)canRotate { }

canRotate does not need to do anything, it just needs to be in my rotatable view controller.

 

- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
  // Get topmost/visible view controller
  UIViewController *currentViewController = [self topViewController];

  // Check whether it implements a dummy methods called canRotate
  if ([currentViewController respondsToSelector:@selector(canRotate)]) {
      // Unlock landscape view orientations for this view controller
      return UIInterfaceOrientationMaskAllButUpsideDown;
  }

  // Only allow portrait (standard behaviour)
  return UIInterfaceOrientationMaskPortrait;
}

That is pretty much it. Now we only need to use two methods to determine the topmost view controller and since the topmost could be encapsulated in a UITabBarController, UINavigationController or could be a modal view controller, the method needs to recursively traverse the view controller hierarchy to determine the visible view controller. The methods have been taken from this stackoverflow post: http://stackoverflow.com/a/17578272/636931

 

- (UIViewController*)topViewController {
  return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}

- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController {
  if ([rootViewController isKindOfClass:[UITabBarController class]]) {
    UITabBarController* tabBarController = (UITabBarController*)rootViewController;
    return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
  } else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
    UINavigationController* navigationController = (UINavigationController*)rootViewController;
    return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
  } else if (rootViewController.presentedViewController) {
    UIViewController* presentedViewController = rootViewController.presentedViewController;
    return [self topViewControllerWithRootViewController:presentedViewController];
  } else {
    return rootViewController;
  }
}

That way we can rotate only one view controller without changing anything in the view controllers that don’t need additional interface orientations. Just add the stub method canRotate to the rotatable view controller e.g. the image gallery and enjoy the rotation :)

That all folks, I hope it helps. Feedback, comments, criticism is appreciated!

Cheers,
Sebastian

Sources:
http://stackoverflow.com/a/12728435/636931
http://stackoverflow.com/a/17578272/636931

Tags: ,


0 Comments


Would you like to share your thoughts?

Would you like to share your thoughts?

Leave a Reply


*