On iOS 7, pushing a controller with a toolbar leaves a gap of unusable space if it's ultimately contained within a tab bar controller

Bill picture Bill · Dec 14, 2013 · Viewed 19.8k times · Source

In my iOS app, my window's rootViewController is a tab bar controller with the a hierarchy like this:

  • UITabBarController
    • UINavigationController 1
      • FirstContentController
    • UINavigationController 2
      • ...
    • UINavigationController 3
      • ...
    • ...

When the user taps a certain row on FirstContentController, an instance of SecondController will be pushed onto its navigation controller. SecondContentController sets hidesBottomBarWhenPushed to YES in its init method and sets self.navigationController.toolbarHidden to NO in viewWillAppear:.

In iOS 6, the user would tap the row in FirstController and SecondController would get pushed onto the nav controller. Because it has hidesBottomBarWhenPushed set, it would hide the tab bar and, by the time the transition animation was complete, SecondController would be on the screen with its toolbar visible.

However, when testing this under iOS 7, hidesBottomBarWhenPushed's behavior seems to have changed. What I see now is:

  • the tab bar hides, as expected
  • the toolbar appears, as expected
  • a gap of unusable space exactly 49 pixels tall (the height of the tab bar) appears between the toolbar and the content view

The gap is completely unusable - it doesn't respond to touches and if i set clipsToBounds to YES on the main view, nothing draws there. After a lot of debugging and examining subview hierarchies, it looks like iOS's autosizing mechanism resizes the view controller's view to a height of 411 (on the iPhone 5). It should be 460 to reach all the way down to the toolbar, but the layout system seems to be including a "ghost" 49-pixel-tall tab bar.

This problem only occurs if the view controller has a tab bar controller as one if its parent containers.

On iOS 7, how can I have the tab bar disappear and a toolbar seamlessly slide into place when a new controller is pushed, and still have the view take up the entire space between the navigation item and the toolbar?

UPDATE

After further investigation, this only happens if SecondController's edgesForExtendedLayout is set to UIRectEdgeNone. However, unless I set that property to UIRectEdgeNone, the view's frame is too long and extends under the toolbar, where it can't be seen or interacted with.

Answer

srik picture srik · Jul 18, 2014

I found that adding the following 2 lines of code in viewDidLoad of SecondViewController (where you want to hide TabBar but show the tool bar) fixes the problem.

self.extendedLayoutIncludesOpaqueBars = YES;
self.edgesForExtendedLayout = UIRectEdgeBottom;

My viewDidLoad of SecondViewController is as follows:

- (void)viewDidLoad {
    [super viewDidLoad];
    // These 2 lines made the difference
    self.extendedLayoutIncludesOpaqueBars = YES;
    self.edgesForExtendedLayout = UIRectEdgeBottom;

    // The usual configuration
    self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
    self.navigationController.navigationBar.translucent = NO;

    self.navigationController.toolbarHidden = NO;
    self.navigationController.toolbar.barStyle = UIBarStyleBlack;
    self.navigationController.toolbar.translucent = NO;
    .
    .
}

But you need to fix the frame of the view manually as this causes the size to be (320x504). Which means it extends even behind the tool bar. If this is not a concern for you then this solution should work.