UIView doesn't resize to full screen when hiding the nav bar & tab bar

I have an app that has a tab bar & nav bar for normal interaction. One of my screens is a large portion of text, so I allow the user to tap to go full screen (sort of like Photos.app).

The nav bar & tab bar are hidden, and I set the the text view's frame to be full screen. The problem is, there is about 50px of white space where the tab bar used to be. You can see if from this screen shot:

removed dead ImageShack link

I'm not sure what's causing this. The whitespace is definitely not the view behind the text view, as I set it's background color to red just to be sure. What could be causing this?

** UPDATE **

I did some hit testing in a UIWindow subclass and found out that the whitespace is actually the undocumented/unpublished UILayoutContainerView. This is the parent view of the tabBar. I don't think it's recommended to directly manipulate this view, so how can I hide the tab bar?

** UPDATE # 2 **

I checked self.view's frame before & after animation, and it looks like the parent view is not resizing enough.

after going fullscreen, the view's frame is only 411 pixels tall. I've tried messing with the frame manually and also setting autoResizeMask with no luck.

**** UPDATE: Here's the end result ****

- (void)toggleFullscreen {
    isFullScreen = !isFullScreen;  //ivar
    
    //hide status bar & navigation bar
    [[UIApplication sharedApplication] setStatusBarHidden:isFullScreen animated:YES];
    [self.navigationController setNavigationBarHidden:isFullScreen animated:YES];
    
    [UIView beginAnimations:@"fullscreen" context:nil];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [UIView setAnimationDuration:.3];
    
    //move tab bar up/down
    CGRect tabBarFrame = self.tabBarController.tabBar.frame;
    int tabBarHeight = tabBarFrame.size.height;
    int offset = isFullScreen ? tabBarHeight : -1 * tabBarHeight;
    int tabBarY = tabBarFrame.origin.y + offset;
    tabBarFrame.origin.y = tabBarY;
    self.tabBarController.tabBar.frame = tabBarFrame;
    
    //fade it in/out
    self.tabBarController.tabBar.alpha = isFullScreen ? 0 : 1;
    
    //resize webview to be full screen / normal
    [webView removeFromSuperview];
    if(isFullScreen) {
                //previousTabBarView is an ivar to hang on to the original view...
        previousTabBarView = self.tabBarController.view;
        [self.tabBarController.view addSubview:webView];

        webView.frame = [self getOrientationRect];  //checks orientation to provide the correct rect
        
    } else {
        [self.view addSubview:webView];
        self.tabBarController.view = previousTabBarView;
    }
    
    [UIView commitAnimations];
}

(note that I switched textview to webview, but the same works for the original text view)


Solution 1:

I had this exact problem where I was animating the tab bar and navigation bar off the bottom and top of the screen respectively, leaving a 49px high white space where the tab bar was.

It turns out that the reason my new "fullscreen" view wasn't actually filling the space was because I was adding the fullscreen view as a subview of the navigation controller's view, which itself was a child of the tab bar controller.

To fix it, I simply added the new fullscreen view (in your case the view with all the text) as a subview of the UITabBarController's view.

[[[self tabBarController] view] addSubview:yourTextView];

Then all you need to do is make sure that your subview's frame is 480 x 320px and it should fill the screen (including the area that was previously the mysterious white space)

Solution 2:

You can definitely make something that appears correct by shifting the frame of the view such that the tab bar is off screen. I know someone else mentioned trying that, and you said it didn't work, but I suspect the issue is that you did not have the textviews resize mask setup properly when you tried. Below is code that should work:

- (void)viewDidLoad {
  [super viewDidLoad];
  currentlyHidden = NO;
}

- (void) hideStuff {
  SO2AppDelegate *appDelegate = (id)[[UIApplication sharedApplication] delegate];
  self.navigationController.navigationBarHidden = YES;

  CGRect newFrame = appDelegate.tabBarController.view.frame;
  newFrame.size.height += appDelegate.tabBarController.tabBar.frame.size.height;
  appDelegate.tabBarController.view.frame = newFrame;
}

- (void) showStuff {
  SO2AppDelegate *appDelegate = (id)[[UIApplication sharedApplication] delegate];

  CGRect newFrame = appDelegate.tabBarController.view.frame;
  newFrame.size.height -= appDelegate.tabBarController.tabBar.frame.size.height;
  appDelegate.tabBarController.view.frame = newFrame;

  self.navigationController.navigationBarHidden = NO;
}

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {  
  if (currentlyHidden) {
    [self showStuff];
    currentlyHidden = NO;
  } else {
    [self hideStuff];
    currentlyHidden = YES;
  }
}

Additionally, since this is definitely sensitive to how you have your nibs etc setup, I am posting a a project online so you can download it and get a look at it. It is a pretty minimal demo, just a Navigation based project where the delegate sets up a tab controller and embeds the view controller from the main nib. The rest is in the RootViewController nib and class. The bars are toggled whenever a touch begins, so just tap the screen. Obviously in a real app you might need to adjust the scroll view so stuff the content doesn't appear to jump.

Solution 3:

Have you set your view controller's hidesBottomBarWhenPushed property to YES? You need to set this in your initWithNibName:bundle: or initWithCoder: method. When it gets pushed to the nav controller, this should hide the tab bar and make the content view extend down to the bottom.

Solution 4:

Maybe there's another view that is a built-in part of the tab bar area? That is to say, something additional to hide. This will help you see what views are around.

for (UIView *viewy in [self.navigationController.view subviews])
{
    NSLog([viewy description]);
}