开发者

hidesBottomBarWhenPushed = NO not working?

I have a UITabBar in my app, which I'm hiding on the first UIViewController in the first tab by putting this line in the AppDelegate:

// ... in MyAppDelegate.m
firstViewController.hidesBottomBarWhenPushed = YES;

In the firstViewController, the user can push a UIButton that pushes a new UIViewController in the same tab. I'd like for the UITabBar to开发者_如何学Python be visible again when this happens. I'm trying to make it come back like this:

//... in firstViewController.m

secondViewController = [[SecondViewController alloc] init];
secondViewController.hidesBottomBarWhenPushed = NO;
[[self navigationController] pushViewController:secondViewController animated:YES];

Unfortunately, does not bring back the UITabBar. It remains hidden.

How do I properly bring bar the UITabBar after hiding it?

Thanks in advance.


This is an issue that has bugged me for a while, and I only just found a solution that works. The hidesBottomBarWhenPushed property is a very strange beast, and works in, to my mind, a counter-intuitive way.

The problem with it is that when you push a new view controller (or pop back) the navigationController will ask all view controllers (from top to bottom) if they want to hide the bottom bar, and if any of them say YES the tabbar will be hidden, which is why the tabbar remains hidden despite setting NO to hiding on the new view controller.

Here is my solution - override the hidesBottomBarWhenPushed getter in the view controller that you wish to not have a tabbar, and check if it is at the top of the stack:

Objective-C

- (BOOL) hidesBottomBarWhenPushed
{
    return (self.navigationController.topViewController == self);
}

Swift (not so obvious, hence snippet)

override var hidesBottomBarWhenPushed: Bool {
    get {
        return navigationController?.topViewController == self
    }
    set {
        super.hidesBottomBarWhenPushed = newValue
    }
}

This nicely encapsulates the hide/show logic in one place, so you dont have to think about it outside of the viewcontroller that does the hiding.


This is what the documentation for hidesBottomBarWhenPushed says (emphasis added):

If YES, the bottom bar remains hidden until the view controller is popped from the stack.

So it looks like the behavior you're seeing is just what the documentation says will happen. You start by pushing a view controller onto the stack which has hidesBottomBarWhenPushed = YES. At that point, pushing other view controllers onto the stack won't change the hiddenness of the bottom bar. As long as that first view controller is on the stack, the bottom bar will remain hidden.

So I think you'll have to come up with a different way of accomplishing your UI goal. One option would be to present the first view controller as a modal view controller over the tab bar controller's view. Then, when you want to go to the second view controller just dismiss the first one and voila. The only visual difference will be the transition animation.

There are surely other options too, but that just came first to my mind.

Good luck!


I had the same issue, but after 3 hours I was found solution! In this topic answered Oct 8 '10, Dave Batton said:

Right way to use hidesBottomBarWhenPushed property is:

self.anotherViewController.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:self.anotherViewController animated:animated];


Not sure if a solution was found for this but I just managed to get this working.

My scenario:

I have a UITabBarController with 4 tab bar items. On one of the tab bar items it loads a UIViewController with buttons on it. The buttons calls a IBOutlet function that loads another UIViewController which contains the a tab bar at the bottom.

After many trial & error........

On the IBOutlet function, I do the following:

{
 self.hidesBottomBarWhenPushed = YES;
 /* Push the new controller with tab bar */
}

This was working fine with the UITabBarController's tab bar sliding to the left and my tab bar from the pushed controller sliding from the right.

Obviously from functionality perspective I need to push the initial UITabBarController's tar bar back in when "going back".

After many trial & error........

I have the method viewWillDisappear in the UIViewController that pushes the UIViewController with tab bar as:

- (void) viewWillDisappear:(BOOL)animated
{
    self.hidesBottomBarWhenPushed = NO;
}

I ran a few quick tests on this in the simulator and it seems to work fine.

Some contributors suggests that this is bad UI but I am trying this out at the moment to see how it works out.

Happy to receive (cop) any feedbacks. :)


I think you misunderstood the hidesBottomBarWhenPushed use. If YES, the bottom bar remains hidden until the view controller is popped from the stack.

So if I understand your question correctly:

The secondViewController should be the YES, the firstViewController should be the NO.


Use

secondViewController.hidesBottomBarWhenPushed = NO;

When insert some operations

firstViewController.hidesBottomBarWhenPushed = YES;


I figured out a really easy to way to solve this by subclassing UINavigationController. You could accomplish the same thing by using a category with an associated object but I already had a subclass so I just threw the code in there.

First, you add an ivar to your UINavigationController:

@interface CustomNavigationController ()
{
    NSMutableSet *_viewControllersWithHiddenBottomBar;
}

@end

Then I overrode the push and pop methods to take over handling the hiding logic:

- (void) pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    if(viewController.hidesBottomBarWhenPushed)
    {
        viewController.hidesBottomBarWhenPushed = NO;
        [_viewControllersWithHiddenBottomBar addObject:viewController];
        [self rootViewController].hidesBottomBarWhenPushed = YES;
    }
    else
    {
        [self rootViewController].hidesBottomBarWhenPushed = NO;
    }
    [super pushViewController:viewController animated:animated];
}

- (UIViewController *) popViewControllerAnimated:(BOOL)animated
{
    if([_viewControllersWithHiddenBottomBar containsObject:self.viewControllers[self.viewControllers.count - 2]])
    {
        [self rootViewController].hidesBottomBarWhenPushed = YES;
    }
    else
    {
        [self rootViewController].hidesBottomBarWhenPushed = NO;
    }
    UIViewController *poppedViewController = [super popViewControllerAnimated:animated];
    [_viewControllersWithHiddenBottomBar removeObject:poppedViewController];
    return poppedViewController;
}

- (UIViewController *) rootViewController
{
    return ((UIViewController *)self.viewControllers.firstObject);
}

I use the hidesButtomBarWhenPushed property to populate the set and then reset the value at the viewcontroller level (since if any view controller has this property set, everything on top of it will also have it hidden). To make things simple I use the root viewcontroller to control showing and hiding the tabbar based on the values in the set.

You also need to initialize the set somewhere, I just used initWithRootViewController:.

This works pretty seamlessly for me and is the least hacky way I could think of to do this, without taking over any existing animations and having to handle edge cases.


Set View controller in

I have no need setting screen in tabbar, this setting screen in past two methods and setting screen to push any screen in display bottom Tabbar.

override func viewWillAppear(_ animated: Bool) {

     self.hidesBottomBarWhenPushed = true
}

override func viewDidAppear(_ animated: Bool) {

    self.hidesBottomBarWhenPushed = false
}

Thanks, if you have any query free to ask.


This one works for me. Thanks to a hint in some other thread here I found a solution to hide the tab bar for one view cotroller only and to re-establish it for any view controller that is called from within.

Doing so, I can keep up the regular chain of navigation controlers.

This is what I finally got:

#define kTabBarHeight               49 // This may be different on retina screens. Frankly, I have not yet tried.

- (void) hideTabBar:(BOOL)hide {

    // fetch the app delegate
    AppDelegate         *delegate   = [[UIApplication sharedApplication] delegate];

    // get the device coordinates
    CGRect              bounds      = [UIScreen mainScreen].bounds;
    float               width;
    float               height;

    // Apparently the tab bar controller's view works with device coordinates  
    // and not with normal view/sub view coordinates
    // Therefore the following statement works for all orientations. 
    width                   = bounds.size.width;
    height                  = bounds.size.height;

    if (hide) {

        // The tab bar should be hidden too. 
        // Otherwise it may flickr up a moment upon rotation or 
        // upon return from detail view controllers. 
        [self.tabBarController.tabBar setHidden:YES];

        // Hiding alone is not sufficient. Hiding alone would leave us with an unusable black
        // bar on the bottom of the size of the tab bar. 
        // We need to enlarge the tab bar controller's view by the height of the tab bar. 
        // Doing so the tab bar, although hidden, appears just beneath the screen. 
        // As the tab bar controller's view works in device coordinations, we need to enlarge 
        // it by the tab bar height in the appropriate direction (height in portrait and width in landscape)
        // and in reverse/upside down orientation we need to shift the area's origin beyond zero. 
        switch (delegate.tabBarController.interfaceOrientation) {
            case UIInterfaceOrientationPortrait:
                // Easy going. Just add the space on the bottom.
                [self.tabBarController.view setFrame:CGRectMake(0,0,width,height+kTabBarHeight)];
                break;

            case UIInterfaceOrientationPortraitUpsideDown:
                // The bottom is now up! Add the appropriate space and shift the rect's origin to y = -49
                [self.tabBarController.view setFrame:CGRectMake(0,-kTabBarHeight,width,height+kTabBarHeight)];
                break;

            case UIInterfaceOrientationLandscapeLeft:
                // Same as Portrait but add the space to the with but the height
                [self.tabBarController.view setFrame:CGRectMake(0,0,width+kTabBarHeight,height)];
                break;

            case UIInterfaceOrientationLandscapeRight:
                // Similar to Upside Down: Add the space and shift the rect. Just use x and with this time
                [self.tabBarController.view setFrame:CGRectMake(0-kTabBarHeight,0,width+kTabBarHeight,height)];
                break;

            default:
                break;
        }
    } else {
        // reset everything to its original state. 
        [self.tabBarController.view setFrame:CGRectMake(0,0,width,height)];
        [self.tabBarController.tabBar setHidden:NO];
    }

    return; 
}


- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{

    // It is important to call this method at all and to call it here and not in willRotateToInterfaceOrientation
    // Otherwise the tab bar will re-appear. 
    [self hideTabBar:YES];

    // You may want to re-arrange any other views according to the new orientation
    // You could, of course, utilize willRotateToInterfaceOrientation instead for your subViews. 
}

- (void)viewWillAppear: (BOOL)animated { 

    // In my app I want to hide the status bar and navigation bar too. 
    // You may not want to do that. If so then skip the next two lines. 
    self.navigationController.navigationBar.barStyle = UIBarStyleBlackTranslucent;
    [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];

    [self hideTabBar: YES];

    // You may want to re-arrange your subviews here. 
    // Orientation may have changed while detail view controllers were visible. 
    // This method is called upon return from pushed and pulled view controllers.   

    return;
}

- (void)viewWillDisappear: (BOOL)animated {     

    // This method is called while this view controller is pulled
    // or when a sub view controller is pushed and becomes visible
    // Therefore the original settings for the tab bar, navigation bar and status bar need to be re-instated

    [self hideTabBar:NO];

    // If you did not change the appearance of the navigation and status bar in viewWillAppear,
    // then you can skip the next two statements too. 
    self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];

    return;
}

The in-line comments should explain the reasoning for each statement. Though, there may be smarter ways of coding it.

There is one side effect in conjunction with hiding the status bar and navigation bar too, which I do not want to hide from you guys. 1. When returning from this navigation controller to the calling navigation controller then the status bar and navigation bar on the calling controller overlap until the device is rotated once or until the related tab has been choosen again after another tab came to front. 2. When the calling view controller is a table view and when the device is in landscape mode when returning to the table, then the table is displayed in the appropriate orientation for landscape but it is layed out as if it were portrait. The upper left corner is fine but some table cells plus tab bar are hidden beneath the screen. On the right hand side there is some free space. This, too, is fixed by rotating the device again.

I will keep you updated once I found solutions for these minor but nasty bugs.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜