On iOS 7, pushing a controller with a toolbar leav

2019-01-30 11:00发布

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.

14条回答
爷的心禁止访问
2楼-- · 2019-01-30 11:28

You will not like this answer This is not the answer you want, but after some research on hiding the tab bar in iOS7, my conclusion is: don't!

Tab bars have never been meant to be hidden - after all why have a UITabBarController if you want to hide the tab bar. The hidesBottomBarWhenPushed on view controllers is for hiding the bottom bar of a navigation controller, not tab bars. From the documentation:

A view controller added as a child of a navigation controller can display an optional toolbar at the bottom of the screen. The value of this property on the topmost view controller determines whether the toolbar is visible. If the value of this property is YES, the toolbar is hidden. If the value of this property is NO, the bar is visible.

Moreover, you are warned not to modify the tab bar object directly. Again, from the documentation:

You should never attempt to manipulate the UITabBar object itself stored in this property.

This is exactly what you are doing when setting it to hidden.

In iOS6 this has worked, but now in iOS7, it doesn't. And it seems very error prone to hide it. When you finally manage to hide it, if the app goes to the background and returns, Apple's layout logic overrides your changes.

My suggestion is to display your data modally. In iOS7 you can create custom transitions, so if it is important to you to have a push transition, you can recreate it yourself, although this is a bit over the top. Normal modal transition is something users are familiar, and actually fits this case better than push which hides the tab bar.


Another solution is to use a toolbar instead of a tab bar. If you use the navigation controller's toolbar for your tabs, you can then use hidesBottomBarWhenPushed as you require and it would give you the behavior you expect.

查看更多
smile是对你的礼貌
3楼-- · 2019-01-30 11:28

I manually manage hide/unhide of bottom-tab-bar along with fade animation by

 ...

[self.tabBarController.tabBar setHidden:NO];

[self.tabBarController.tabBar setAlpha:0.1];
[UIView animateWithDuration:0.2 animations:^{
    [self.tabBarController.tabBar setAlpha:1.0];
}];
 ...

Bottom Toolbar on SecondVC was added in IB. No problem so far. Using Storyboard.

查看更多
贪生不怕死
4楼-- · 2019-01-30 11:30

You do have to set the tabBar of the TabBarController to hidden and your view should have autosizing set to flexible height.

With this code it's working:

@implementation SecondController

-(id)init
{
    if( (self = [super init]) )
    {
    }

    return self;
}

- (void)viewDidLoad;
{
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor redColor];
    self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight;
    self.tabBarController.tabBar.hidden = YES;
}

-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    // will log a height of 411, instead of the desired 460
    NSLog(@"frame: %@", NSStringFromCGRect(self.view.frame));
}

@end

Or, if you do want to use the hidesBottomBarWhenPushed method, you have to do this before you push the view controller obviously:

-(void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
    SecondController* controller = [[SecondController alloc] init];
    controller.hidesBottomBarWhenPushed = YES;

    [self.navigationController pushViewController:controller animated:YES];
}

If using the second method, your viewDidLoad method can get rid of flexible height method as well as tabBarHidden:

- (void)viewDidLoad;
{
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor redColor];
    self.edgesForExtendedLayout = UIRectEdgeNone;
}

See the result:

enter image description here

查看更多
走好不送
5楼-- · 2019-01-30 11:32

Have you tried to move your call hidesBottomBarWhenPushed in the viewDidLoad or before the secondViewController is pushed?

With ios7, a lot of timing issues appear if you don't do the calls at teh good moment.

查看更多
姐就是有狂的资本
6楼-- · 2019-01-30 11:34

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.

查看更多
The star\"
7楼-- · 2019-01-30 11:34

As @Leo Natan is pointing out, it seems as if hiding the tab bar and showing a toolbar is discouraged. Nevertheless, there is a very easy solution that is working:

Just check "Under Opaque Bars" in the view controller properties in the storyboard as shown below:

查看更多
登录 后发表回答