1
votes

All week I have been searching SO for a solution to my unique problem, but nothing has worked for me yet. I have a TabBarController that has 6 tabs. iOS automatically creates the "More" tab, which contains the view controller,"Dashboard", that I am having difficulty with. The "Dashboard" view controller is acting as a page controller that has 3 pages.

When I press the "Dashboard" item, I want the screen orientation to change from Portrait to Landscape. I've learned through countless other SO posts that rotation is controlled from the parent TabBarController. I have also learned that the "More" tab is actually a navigation controller, which adds some more complication to the mix.

To reiterate and be direct, when I press the "Dashboard" tab in the TabBarController's "More" tab, I want the screen to change its orientation from Portrait to Landscape.

And YES, I have already tried this: [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger: UIInterfaceOrientationLandscapeLeft]forKey:@"orientation"]; and it didn't work.

I've also tried overriding every function in the TabBarController and MoreNavigationController that has to do with rotation and orientation, as well as in the child views. I will post my TabBarController below. Any help will be appreciated, thanks.

TabBarViewController.h:

@interface TabBarViewController : UITabBarController<UINavigationControllerDelegate>{}
@end

TabBarViewController.m:

@implementation TabBarViewController
- (void)viewDidLoad
{
    [super viewDidLoad];
    self.moreNavigationController.delegate = self;
}

- (BOOL)shouldAutorotate
{
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations
{
    if([self.selectedViewController isKindOfClass:[Dashboard class]])
    {
        return UIInterfaceOrientationMaskLandscape;
    }
    return UIInterfaceOrientationMaskAll;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
    return YES;
}

- (NSUInteger)tabBarControllerSupportedInterfaceOrientations:(UITabBarController *)tabBarController
{
    if([tabBarController.selectedViewController isKindOfClass:[Dashboard class]])
    {
        return UIInterfaceOrientationMaskLandscape;
    }

    return UIInterfaceOrientationMaskAll;
}

- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{

    NSLog(@"tabBarController didSelectViewController = %lu", (unsigned long)tabBarController.selectedIndex);

}

- (NSUInteger)navigationControllerSupportedInterfaceOrientations:(UINavigationController *)navigationController
{
    if([navigationController.topViewController isKindOfClass:[Dashboard class]]
       || [navigationController.topViewController isKindOfClass:[ViewController1 class]]
       || [navigationController.topViewController isKindOfClass:[ViewController2 class]]
       || [navigationController.topViewController isKindOfClass:[ViewController3 class]])
    {
        return UIInterfaceOrientationMaskLandscape;
    }
    return UIInterfaceOrientationMaskAllButUpsideDown;
}

- (UIInterfaceOrientation)navigationControllerPreferredInterfaceOrientationForPresentation:(UINavigationController *)navigationController
{
    NSLog(@"navigationControllerPreferredInterfaceOrientationForPresentation");
    if([navigationController.topViewController isKindOfClass:[Dashboard class]]
       || [navigationController.topViewController isKindOfClass:[ViewController1 class]]
       || [navigationController.topViewController isKindOfClass:[ViewController2 class]]
       || [navigationController.topViewController isKindOfClass:[ViewController3 class]])
    {
        return UIInterfaceOrientationLandscapeLeft | UIInterfaceOrientationLandscapeRight;
    }
    return UIInterfaceOrientationPortrait|UIInterfaceOrientationMaskLandscapeRight|UIInterfaceOrientationMaskLandscapeLeft;
}

- (void)navigationController:(UINavigationController *)navigationController
      willShowViewController:(UIViewController *)viewController
                    animated:(BOOL)animated
{
    if([viewController isKindOfClass:[Dashboard class]])
    {
        [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger: UIInterfaceOrientationLandscapeLeft]forKey:@"orientation"];
    }
}

@end
1
Have you found a solution?SwiftArchitect
From my own research and understanding of what I have read, TabBarControllers can't rotate. So what I did instead was have a Portrait viewController for the "Dashboard" that listened for changes in orientation. When the orientation changes to landscape, I modally present my page view controllererics
I guess you could try to have the TabBarViewController modally present the pageViewController when "Dashboard" is selected, instead of trying to rotate the device......erics
TabBarViewController, for one reason or another, needs a trigger to rotate, but it can be done. stackoverflow.com/a/32390717/218152 shows how to achieve orientation changes on TabBarViewController without being modal.SwiftArchitect

1 Answers

0
votes

Changing the orientation of a UITabBarController

While there are dozens of maybe and try this for this problem, the main issue reside in the careful design of each UINavigationController, a proper response to supportedInterfaceOrientations in the UITabBarController, and some code to enforce the desired orientation.

In all likelihood, this may just be an iOS bug, since once rotated, the UINavBarController and all embedded UINavigationController behave correctly.

The following statement is not true. Tab bar controllers do rotate.

TabBarControllers can't rotate


Setup orientation for each UINavigationController

Landscape example:

class HorizontalNavigationController: UINavigationController {
    override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
        return .landscape
    }
}

Portrait example:

class VerticalNavigationController: UINavigationController {
    override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
        return .portrait
    }
}

Defer TabBarController orientation to selected view controller

class TabBarController: UITabBarController {
    override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
        if let selectedViewController = selectedViewController {
            return selectedViewController.supportedInterfaceOrientations
        }
        return .allButUpsideDown
    }
}

Enforce the interface orientation change

This is the contentious aspect of UITabBarController. Somehow, the orientation change does not happen like it would in a UINavigationController, and needs a nudge.

To do so with the fewest amount of code, use an Object and subclass it into a TabBarControllerDelegate. You can do most of the work in Interface Builder.

class TabBarControllerDelegate: NSObject, UITabBarControllerDelegate {
    func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
        let orientedViewController = UIViewController()

        tabBarController.present(orientedViewController, animated: false) { () -> Void in
            tabBarController.dismiss(animated: false, completion: { () -> Void in
            })
        }
    }
}

Storyboard Structure

Using a Storyboard helps visualize the structure of the application as well as the relationship between all classes.

Storyboard


► Find this solution on GitHub and additional details on Swift Recipes.