34

I have an application that uses a UITabBarController to switch between modes. When in a certain mode, I'd like to hide the tab bar until the steps of that mode have been completed. Note that I'm not using a navigation controller so I can't use the setHidesBottomBarWhenPushed method on the navigation controller to hide the tab bar.

Prior to iOS 8, When I attempt to hide the tarbar using:

self.tabBarController.tabBar.hidden = YES

the tab bar goes away, but it leaves a 50 pixel blank area at the bottom of the screen where the tab bar used to be. I can't seem to figure out how to fill that area. Anything in the UI that is in that area is clipped and cannot be seen.

Any ideas if this is even possible? I'd really like to stay away from the navigation controller.

BadPirate
  • 25,802
  • 10
  • 92
  • 123
Steve
  • 2,678
  • 8
  • 40
  • 54
  • 3
    One thing that seems to get the visual behavior that I want is to simply change the frame of the TabBarController to (0,0,320,530). This pushes the tab bar off the bottom of the display, and allows the use of the full screen. Not exactly a perfect solution, but it seems to work until something better comes along. – Steve Dec 30 '09 at 21:18
  • Thanks for that idea. It's ugly, but it does the trick. – Dimitris Mar 30 '10 at 15:29
  • Hi, how do I get the tabBar back in future screens? – quantumpotato Aug 16 '10 at 15:13
  • 1
    @quantumpotato Just reset the frame bounds back to (0,0,320,480) to move the tabbar back up. – Steve Dec 22 '10 at 17:26
  • 3
    Note that in iOS 8, this .tabBar.hidden = true works fine, no issue at all with dead/blank screen area. – Bill Patterson Aug 29 '15 at 17:03
  • Just use a navigation controller. Hacking the system is not a good idea – doozMen Oct 06 '15 at 09:57

16 Answers16

37

Here's my code for that:

This is, of course, mucking with the goings on in the controller's view hierarchy. It could change/break. This uses defined APIs, so Apple won't care, but they won't care about breaking your code, either.

- (void)hideTabBar {
  UITabBar *tabBar = self.tabBarController.tabBar;
  UIView *parent = tabBar.superview; // UILayoutContainerView
  UIView *content = [parent.subviews objectAtIndex:0];  // UITransitionView
  UIView *window = parent.superview;

  [UIView animateWithDuration:0.5
                   animations:^{
                     CGRect tabFrame = tabBar.frame;
                     tabFrame.origin.y = CGRectGetMaxY(window.bounds);
                     tabBar.frame = tabFrame;
                     content.frame = window.bounds;
                   }];

  // 1
}

- (void)showTabBar {
  UITabBar *tabBar = self.tabBarController.tabBar;
  UIView *parent = tabBar.superview; // UILayoutContainerView
  UIView *content = [parent.subviews objectAtIndex:0];  // UITransitionView
  UIView *window = parent.superview;

  [UIView animateWithDuration:0.5
                   animations:^{
                     CGRect tabFrame = tabBar.frame;
                     tabFrame.origin.y = CGRectGetMaxY(window.bounds) - CGRectGetHeight(tabBar.frame);
                     tabBar.frame = tabFrame;

                     CGRect contentFrame = content.frame;
                     contentFrame.size.height -= tabFrame.size.height;
                   }];

  // 2
}

Edit: An anonymous user has suggested the following addition for 7.0 (i have not tested this, and could not say whether it is a workaround or an ideal implementation):

// 1. To Hide the black line in IOS7 only, this extra bit is required
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")) {
    [self.tabBarController.tabBar setTranslucent:YES];
}  

// 2. For IOS 7 only
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")) {
    [self.tabBarController.tabBar setTranslucent:NO];
}

Edit: Entirely untested in 8.x and likely lacking in some layouts.

bshirley
  • 8,217
  • 1
  • 37
  • 43
  • 4
    This is great! The old way we accomplished this caused an unresponsive portion of the screen where the tab bar used to be, but this method does not. I would suggest changing the animation duration to `UINavigationControllerHideShowBarDuration` if you are also hiding the navbar at the same time. – Chris Ballinger Mar 15 '12 at 17:05
  • 3
    Actually, it turns out that this method causes problems with my subview's frames so I had to use the `hidesBottomBarWhenPushed` attribute instead. – Chris Ballinger Mar 15 '12 at 20:05
  • perhaps changing the resizing behaviors of your views would cause the problems from not happening? hard to say, not knowing what the actual problems are. – bshirley Mar 20 '12 at 22:34
  • This is great job! Mr.bshirley – SampathKumar Aug 17 '12 at 17:00
  • It causes problems with other subviews. – Salih Ozdemir May 10 '14 at 23:27
  • This solution left black space in the bottom. these http://stackoverflow.com/a/4470319/1698467solution works fine! – skywinder Nov 20 '14 at 14:13
9

I tried a number of the solutions above, but no joy in iOS 8. I find that setting in viewWillAppear the following works for me. Should work in iOS 7 as the extendedLayoutIncludesOpaqueBars was introduced then.

    self.extendedLayoutIncludesOpaqueBars = true
    self.tabBarController?.tabBar.isHidden = true
    self.tabBarController?.tabBar.isOpaque = true

and if you need to turn tabBars on again when you leave to use the following in viewWillDisappear.

    self.tabBarController?.tabBar.isHidden = false
    self.tabBarController?.tabBar.isOpaque = false

I use this to allow a return from a transition to keep the TabBar hidden. Not used it in a button action but if like me you find nothing above now works, this could be the basis of a programmable solution.

Damien Debin
  • 2,812
  • 25
  • 41
GAllan
  • 409
  • 4
  • 11
9

Like Steve, I haven't found a clean way to do this (even though Apple Photopicker does something similar). Here is what I have done:

 if (systemAction)
  {
    // Reveal tab bar back
    CGRect bounds = [[UIScreen mainScreen] bounds];
    CGRect tabBarFrame = self.tabBarController.tabBar.frame;
    self.tabBarController.view.frame = CGRectMake(0,0,bounds.size.width,bounds.size.height);
    self.toolBar.hidden = YES;
    systemAction = NO;
  }
  else
  {
    //hide tab bar
    CGRect bounds = [[UIScreen mainScreen] bounds];
    CGRect tabBarFrame = self.tabBarController.tabBar.frame;
    CGRect navigationBarFrame = self.navigationController.navigationBar.frame;
    self.tabBarController.view.frame = CGRectMake(0,0,bounds.size.width,bounds.size.height+tabBarFrame.size.height);
    self.toolBar.hidden = NO;
    CGRect frame = self.toolBar.frame;
    frame.origin.y = bounds.size.height - frame.size.height - navigationBarFrame.size.height;
    self.toolBar.frame = frame;
    systemAction = YES;
  }

What it is doing is pushing the view down so I can display a toolbar (and not hiding it). Obviously this is for only the 'root view' of a tabbar + navigation controller. For any subsequent views you can set the 'hidesBottomBarWhenPushed' on the viewcontroller you are pushing.

Terence
  • 361
  • 4
  • 6
4

It's a bit late in the day, but of all the answers to the question that I've trawled through this afternoon, this is the one that worked best for me.

How to hide uitabbarcontroller

// Method call
[self hideTabBar:self.tabBarController];   

// Method implementations
- (void)hideTabBar:(UITabBarController *) tabbarcontroller
{
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.5];

    for(UIView *view in tabbarcontroller.view.subviews)
    {
        if([view isKindOfClass:[UITabBar class]])
        {
            [view setFrame:CGRectMake(view.frame.origin.x, 480, view.frame.size.width, view.frame.size.height)];
        } 
        else 
        {
            [view setFrame:CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, 480)];
        }
    }

    [UIView commitAnimations];   
}

- (void)showTabBar:(UITabBarController *) tabbarcontroller
{       
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.5];
    for(UIView *view in tabbarcontroller.view.subviews)
    {
        NSLog(@"%@", view);

        if([view isKindOfClass:[UITabBar class]])
        {
            [view setFrame:CGRectMake(view.frame.origin.x, 431, view.frame.size.width, view.frame.size.height)];

        } 
        else 
        {
            [view setFrame:CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, 431)];
        }
    }

    [UIView commitAnimations]; 
}
Community
  • 1
  • 1
Ted
  • 2,525
  • 2
  • 37
  • 54
  • Magic constants in the code! very bad example, especially after iPhone 6(+) released. `[[UIScreen mainScreen] bounds]` is much better to set proper size. – skywinder Nov 20 '14 at 13:31
4

I use only this single line to achieve this. I use prepareForSegue method before showing the view controller having the tab bar.

-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
    if([segue.identifier isEqualToString:@"showLogin"]){
        [segue.destinationViewController setHidesBottomBarWhenPushed:YES];
    }
}
coder9
  • 1,571
  • 1
  • 27
  • 52
3

I had worked on almost the same case, actually used the code from http://www.developers-life.com/hide-uitabbarcontrolleruitabbar-with-animation.html and made it better according to my needs, this might help others too.

I am using a UISplitViewController as the root view controller and its detail portion is a UITabBarController, I had to hide the tabbar in portrait mode:

// In UITabBarController's custom implementation add following method, 
// this method is all that will do the trick, just call this method 
// whenever tabbar needs to be hidden/shown 
- (void) hidetabbar:(NSNumber*)isHidden {
    UITabBarController *tabBarController=self;

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.5];

    CGRect tabbarFrame=CGRectZero;
    for(UIView *theView in tabBarController.view.subviews) {
        //NSLog(@"%@", view);
        if([theView isKindOfClass:[UITabBar class]]) {
            tabbarFrame=theView.frame;
            if ([isHidden boolValue]) {
                tabbarFrame=CGRectMake(tabbarFrame.origin.x, 
                                       tabBarController.view.frame.size.height, 
                                       tabbarFrame.size.width, 
                                       tabbarFrame.size.height);
            } else {
                tabbarFrame=CGRectMake(tabbarFrame.origin.x, 
                                       tabBarController.view.frame.size.height - tabbarFrame.size.height, 
                                       tabbarFrame.size.width,
                                       tabbarFrame.size.height);
            }
            theView.frame=tabbarFrame;
            break;
        }
    }

    for(UIView *theView in tabBarController.view.subviews) {
        if(![theView isKindOfClass:[UITabBar class]]) {
            CGRect theViewFrame=theView.frame;
            if ([isHidden boolValue]) {
                theViewFrame=CGRectMake(theViewFrame.origin.x, 
                                        theViewFrame.origin.y, 
                                        theViewFrame.size.width, 
                                        theViewFrame.size.height + tabbarFrame.size.height);
            } else {
                theViewFrame=CGRectMake(theViewFrame.origin.x, 
                                        theViewFrame.origin.y, 
                                        theViewFrame.size.width, 
                                        theViewFrame.size.height - tabbarFrame.size.height);
            }
            theView.frame=theViewFrame;
        }
    }
    [UIView commitAnimations];
}

I used following code to call the hidetabbar: method

//In my UISplitViewController's custom implementation
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    @synchronized(self){
    //change the self.splitDetailController to your UITabBarController's object
    [self.splitDetailController 
     performSelector:@selector(hidetabbar:) 
     withObject:[NSNumber numberWithBool:UIInterfaceOrientationIsLandscape(interfaceOrientation)]
     afterDelay:0.5];
    }
    return YES;
}

I tested this code to work in simulator only, let me know if it works on device too ;-)

Abduliam Rehmanius
  • 928
  • 12
  • 23
0

autoresizing mask has an enumeration. Try to set all the options and check if autoresize subviews option is checked in parent view

user2159978
  • 2,629
  • 2
  • 16
  • 14
0

Do you have the autoResizingMask set on the sub view?

view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

Something like that should do the trick and allow the view sitting atop the stack to re-size.

MystikSpiral
  • 5,018
  • 27
  • 22
  • Just tried that. No effect. I still get a blank 50 pixel region at the bottom of the screen – Steve Dec 30 '09 at 20:20
0

You can create Tabbar Category and show/Hide easily. and you can access full view.

create category #import "UITabBarController+HideTabBar.h"

@implementation UITabBarController (HideTabBar)

- (void)hideTabBarAnimated:(BOOL)animated
{
    CGRect statusbarFrame = [UIApplication sharedApplication].statusBarFrame;
    CGRect tabBarControllerFrame = self.view.frame;
    if (statusbarFrame.size.height>20)
    {
        tabBarControllerFrame.size.height =  screenSize.size.height + self.tabBar.frame.size.height - 20.0;
    }
    else
    {
        tabBarControllerFrame.size.height = screenSize.size.height + self.tabBar.frame.size.height ;
    }

    if (animated) {
        [UIView animateWithDuration:0.2 animations:^{
            [self.view setFrame:tabBarControllerFrame];
        } completion:^(BOOL finished) {

        }];
    }
    else
        [self.view setFrame:tabBarControllerFrame];
}

- (void)showTabBarAnimated:(BOOL)animated {
    CGRect statusbarFrame = [UIApplication sharedApplication].statusBarFrame;
    CGRect tabBarControllerFrame = self.view.frame;
    if (statusbarFrame.size.height>20)
    {
        tabBarControllerFrame.size.height =  screenSize.size.height - 20.0;
    }
    else
    {
        tabBarControllerFrame.size.height = screenSize.size.height ;
    }

    if (animated) {
        [UIView animateWithDuration:0.2 animations:^{
            [self.view setFrame:tabBarControllerFrame];
        } completion:^(BOOL finished) {

        }];
    }
    else
        [self.view setFrame:tabBarControllerFrame];
}
@end

Note : use statusbarFrame is used when hotspot or call is ON so tabbar would not cut down.

Now Import category in which you class you want to use methods and just call below methods to hide or show tabbar.

[self.tabBarController hideTabBarAnimated:YES];

[self.tabBarController showTabBarAnimated:YES];

Hope this Helps.

ChintaN -Maddy- Ramani
  • 5,156
  • 1
  • 27
  • 48
0

Hope this works.

@interface UITabBarController (Additions)

-(void)setTabBarHidden:(BOOL)hidden animated:(BOOL)animated;

@end

@implementation UITabBarController (Additions)

-(void)setTabBarHidden:(BOOL)hidden animated:(BOOL)animated
{
    if (animated)
    {
        [UIView beginAnimations:nil context:nil];
    }
    if (hidden)
    {

        self.tabBar.frame = CGRectMake(self.tabBar.frame.origin.x, self.tabBar.superview.frame.size.height, self.tabBar.bounds.size.width, self.tabBar.bounds.size.height);
    }
    else
    {
        self.tabBar.frame = CGRectMake(self.tabBar.frame.origin.x, self.tabBar.superview.frame.size.height - self.tabBar.frame.size.height + 10, self.tabBar.bounds.size.width, self.tabBar.bounds.size.height);
    }
    if (animated)
    {
        [UIView commitAnimations];
    }

}

Amit Tandel
  • 883
  • 7
  • 16
0

Here is my solution (my tab view controller is inside navigation controller for good measure)... So I have subclassed UITabBarController and did this... exposing -setTabBarHidden: method

- (void)setTabBarHidden:(BOOL)hidden {
    _tabBarHidden = hidden;

    [UIView performWithoutAnimation:^{
        [self adjustViews];
    }];

}

- (void)adjustViews {
    if ( _tabBarHidden ) {
        CGRect f = self.tabBar.frame;

        // move tab bar offscreen
        f.origin.y = CGRectGetMaxY(self.view.frame);
        self.tabBar.frame = f;

        // adjust current view frame
        self.selectedViewController.view.frame = self.view.frame;
    } else {
        CGRect f = self.tabBar.frame;

        // move tab bar on screen
        f.origin.y = CGRectGetMaxY(self.view.frame) - (CGRectGetMaxY(self.tabBar.bounds) + CGRectGetMaxY(self.navigationController.navigationBar.frame));
        self.tabBar.frame = f;

        // adjust current view frame
        f = self.view.bounds;
        f.size.height -= CGRectGetMaxY(self.tabBar.bounds);
        self.selectedViewController.view.frame = f;
    }
}

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];

    [UIView performWithoutAnimation:^{
        [self adjustViews];
    }];
}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    [UIView performWithoutAnimation:^{
        [self adjustViews];
    }];
}
Cherpak Evgeny
  • 2,659
  • 22
  • 29
0

The obvious solution, keeping your original architecture, would have been to present that view modally:

- (void)tabBarController:(UITabBarController *)tb
 didSelectViewController:(UIViewController *)vc {
    if (tb.selectedIndex == MODALONE) {
        UIViewController* mod = 
            [[UIViewController alloc] initWithNibName: @"ModalView" 
                                               bundle: nil];
        [tb presentModalViewController:mod animated:NO];
        [mod release];
    }
}

The view now covers the entire screen (except for the status bar is there is one) including the tab bar, so it looks as if the tab bar has gone away in response to the user pressing that tab bar item.

matt
  • 515,959
  • 87
  • 875
  • 1,141
0

put the statement in the init method of the UIViewController

override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        self.hidesBottomBarWhenPushed = true
        setupDependencyConfigurator()
    }
-1

See this thread:

Show/Hide TabBarController in iphone

In summary, you can see an example of this behavior in this sample code:

http://developer.apple.com/iphone/library/samplecode/TheElements/index.html

Community
  • 1
  • 1
esilver
  • 27,713
  • 23
  • 122
  • 168
  • Unfortunately, that requires the use of the Navigation Controller, to hide the tab bar when a new view controller is pushed. I'm not using a navigation controller, so that won't work for me. – Steve Jan 11 '10 at 21:52
-3

Why are you not using a navigation controller. It's a lot easier to hide the nav bar than the tab bar...

Kendall Helmstetter Gelner
  • 74,769
  • 26
  • 128
  • 150
  • I've got a fairly tight deadline on the demo version of this project, and I'd rather not have to step backward to get it going. Once the demo version is out the door, and funding pouring in the door :-) I'll most likely completely rewrite the UI and use a nav controller then. – Steve Dec 30 '09 at 23:50
-3

Just made the following code in Monotouch inside a subclass of UITabBarController:

    public void ShowTabBar()
    {
        UIView.BeginAnimations("Anim");
        UIView.SetAnimationDuration(0.25f);
        this.View.Subviews[0].Frame = new RectangleF(0f, 0f, 320f, 431f);
        this.TabBar.Frame = new RectangleF(0f, 431f, 320f, 49f);
        this.TabBar.Hidden = false;
        UIView.CommitAnimations();
    }

    public void HideTabBar()
    {
        UIView.BeginAnimations("Anim");
        UIView.SetAnimationDuration(0.25f);
        this.View.Subviews[0].Frame = new RectangleF(0f, 0f, 320f, 480f);
        this.TabBar.Frame = new RectangleF(0f, 481f, 320f, 510f);
        this.TabBar.Hidden = true;
        UIView.CommitAnimations();          
    }