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

16

37

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)

Foredate answered 10/7, 2009 at 14:59 Comment(1)
Your image link seems to have broken. If you still have the original image, please reupload it to stack.imgur, or just edit your question to make it work without the image. Thanks.Locksmith
F
31

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)

Frostbitten answered 22/7, 2009 at 19:21 Comment(2)
this sort of worked, but I found no easy way to go back to the regular view. self.tabBarController.view = previousView seemed to have weird side effects...Foredate
I eventually got this method working. Rotation really made this act a little weird, so I ended up just setting the web view's frame manually when in full screen mode.Foredate
I
5

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.

Inexplicit answered 22/7, 2009 at 7:44 Comment(1)
I tried your sample and it worked, but for some reason I was unable to translate it into my application. It looked like I was doing the same thing (albeit not via the app delegate, I did it view self.tabBarController). What I ended up with is adding the textview to the tabBarController's view & sizing it up manually. In any case, I appreciate the detailed answer. I wish I could give credit to both of you.Foredate
F
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.

Finical answered 20/7, 2009 at 19:38 Comment(1)
That works, but is not the behavior I want. I need to be able to go in & out of fullscreen with a tap. This works for the nav/status bars, but not for the tab bar.Foredate
C
2

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]);
}
Clegg answered 19/7, 2009 at 6:22 Comment(2)
The views: <UINavigationTransitionView: 0xd2c6f0; frame = (0 0; 320 431); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0xd2bf90>> <UINavigationBar: 0xd1f010; frame = (0 20; 320 44); clipsToBounds = YES; opaque = NO; autoresize = W; layer = <CALayer: 0xd1f060>> <UITransitionView: 0xd19050; frame = (0 0; 320 431); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0xd18a80>> <UITabBar: 0xd1f980; frame = (0 431; 320 49); autoresize = W+TM; layer = <CALayer: 0xd1f520>> (this shows both navigationController subviews and tabBarController subviews)Foredate
This gave me an idea to do a hit test in the white region & see what view came up. I updated the question with more details.Foredate
M
2

I found that if you mark the new view you will be pushing onto the navigation stack with the following line hides the tab bar as long as it is on the stack.

scrollViewController.hidesBottomBarWhenPushed = YES;

Murchison answered 3/10, 2010 at 0:2 Comment(0)
O
2

I had similar issue. As Harry mentioned it's a reason of Navigation Controller that is inner controller of Tab Bar Controller. Found another way how to redraw your view and have correct frame., In your controller add following:

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

    // Here you can make your tabBarControlelr.view.hidde = true or use any animation that hides UITabBar. I made
    [TabBarController setTabBarHidden:YES];

    self.navigationController.view.frame = CGRectMake(self.navigationController.view.frame.origin.x, self.navigationController.view.frame.origin.y, self.navigationController.view.frame.size.width, self.navigationController.view.frame.size.height + 50);
    [self.navigationController.view setNeedsLayout];

 }

And here is how I hide it. I can make it directly calling method of TabBarControlelr or class method wrapper:

+ (void)setTabBarHidden:(BOOL)hidden
{
    AppDelegate *delegate = [UIApplication sharedApplication].delegate;
    TabBarController *tabBarController = (TabBarController *) delegate.window.rootViewController;

    if ([tabBarController isKindOfClass:[TabBarController class]]) {
        [tabBarController setTabBarHidden:hidden];
    }
}


- (void)setTabBarHidden:(BOOL)hidden
{
    if (self.tabBar.hidden == hidden) {
        return;
    }

    if (hidden == NO) {
        self.tabBar.hidden = hidden;
    }

    [UIView animateWithDuration:0.15 animations:^{
        self.tabBar.frame = CGRectOffset(self.tabBar.frame, 0, ( hidden ? 50 : -50 ));
    } completion:^(BOOL finished) {
        self.tabBar.hidden = hidden;
    }];
}
Omaomaha answered 12/11, 2014 at 11:31 Comment(0)
C
1

I believe the issue is that you're still in the tab bar controller, I've hit a few issues with this as well, and ended up creating two views for my app delegate to control, the tab bar controller , and a separate uiview that holds anything that the tab bar controller tries to take command of. You could pass your view to the app delegate and use it there perhaps?

Creaturely answered 20/7, 2009 at 14:49 Comment(1)
I'm not sure I follow... are you saying that when I go fullscreen to use a different view altogether? I tried adding the view directly to the UIWindow, and it looks good, but the experience is rather quirky when you rotate. I'm not sure I want to go this route.Foredate
F
1

Try making the TabBarControllers view larger than the screen so that the tab bar is hidden off screen, as long as you do this before the new ViewController is set, then its view will resize to fill the new frame!

In my test I used the tabBarController:shouldSelectViewController: delegate method to check which view controller is about to become current and set the TabBarController.view.frame accordingly. For example:

- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
    if( viewController == second)
        tabBarController.view.frame = CGRectMake( 0, 20, 320, 500);
    else
        tabBarController.view.frame = CGRectMake( 0, 20, 320, 460);
}

If this still isn't quite what you need, try looking into the hidesBottomBarWhenPushed property of UIViewController. When set to yes this will make the bottom bar hide if the Controller is pushed onto a Navigation stack.

Feces answered 20/7, 2009 at 16:40 Comment(0)
E
1

I would move the view to the window when you choose to go fullscreen. e.g.

myView = [self.view retain];
self.view = nil;
[window addSubview:myView];

and to go back:

[myView removeFromSuperView];
self.view = myView;
[myView release];
myView = nil;

By adding the view as a child of the window, it can be totally fullscreen and will be on top of the tab bar.

You can then combine this with

[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRotate:) name:@"UIDeviceOrientationDidChangeNotification" object:nil];

to detect rotation. (I think that you combine this with view.transform = CGAffineTransformMakeRotation to make it rotate...?)

Ectomere answered 20/7, 2009 at 19:33 Comment(2)
any tips on getting rotation to work when I do this? It doesn't seem to call my shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation method if I do this.Foredate
You can use the current solution combined with: [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRotate:) name:@"UIDeviceOrientationDidChangeNotification" object:nil]; to detect rotation. (I think that you combine this with view.transform = CGAffineTransformMakeRotation to make it rotate...?) You could call your shouldAutorotateToInterface... function from the didRotate: function to see if you should do it, too.Ectomere
R
0

If you are using Interface Builder, ensure autoresizing is set properly in the Size Inspector. If you creating the UITextView in code, make sure you set the frame to be large enough and that the view's parent (and it's parent) are also large enough. For simplicity, add the view to the window directly, and then move inward from there.

To move a view to its superview:

- (void)moveViewToSuperview:(UIView *)view
{
    UIView *newSuperview = [[view superview] superview];
    [view removeFromSuperview];
    [newSuperview addSubview:view];
    [newSuperview bringSubviewToFront:view];
}
Robustious answered 19/7, 2009 at 5:55 Comment(3)
Auto-size is W+H. textView.frame = CGRectMake(0,0, 320, 700); seems to have no effect on it.Foredate
If I remove the textview from the view and then add it directly to the window then it goes full screen. This has weird auto-rotate effects though, so I can't leave it this way. It does seem to indicate that something is occluding the text view.Foredate
Try this: Return your UITextView to its original location in Interface Builder. Add a call to moveViewToSuperview:myTextView just after the view becomes visible run your application. Test to see if it's obscured. If not, add another call and debug again. Rinse repeat. That should help you figure out "how many levels out" the view that's blocking the text view is.Robustious
X
0

Have you attempted to force the text to "re-write" itself (resetting the .text property of whatever object you're using)? I have had many instances where views simply do not re-check or repaint their data, and forcing seems to be the only fix. If this is not good from a user experience standpoint, you might try making the nav/tabbars transparent after the user touches, and before they disappear. This normally extends the view to the full size of the screen.

Xenophon answered 21/7, 2009 at 3:31 Comment(1)
If I make the status bar transparent & use the setWantsFullScreenLayout property to YES then it does it for the top, but not the bottom tab bar.Foredate
B
0

If the UITabBarController is pushed by a rootViewController (navigationController). You can try the following:

First hide the status Bar / Tab Bar;

[self.navigationController pushViewController:aBlankViewController animated:NO];
[self.navigationController popViewControllerAnimated:NO];

You should be able to reclaim the white space, though the screen will shock during the adjustment...

Breeder answered 22/10, 2010 at 15:6 Comment(0)
R
0

I've experienced a similar issue, this discussion was helpful: UIWebView on iPad size

Tony's answer helped me discover that when you create a subview it's helpful to setup code similar to this:

self.tabBarController.tabBar.frame = self.view.bounds;
Robillard answered 1/8, 2011 at 11:42 Comment(0)
J
0

Put the code in a prepare for segue like this, works perfectly for me:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:@"ViewPhoto"]) {

    ImageView *vc = [segue destinationViewController];

    NSInteger selectedIndex = [[self.tableView indexPathForSelectedRow] row];

        NSString *Title = [photoNames objectAtIndex:selectedIndex];
        [vc setSelectedTitle:[NSString stringWithFormat:@"%@", Title]];

        vc.hidesBottomBarWhenPushed = YES;

        }
    }
Jemmy answered 15/12, 2011 at 1:14 Comment(0)
L
0

This works for me when hiding the tab bar and resizing the view

self.tabBarController?.tabBar.isHidden = true

tick the hide bottom bar on push and set presentatoin as over current context

Set properties as folows

Latticework answered 26/4, 2023 at 1:15 Comment(0)
C
-1

Add the extra amt of space to the dimension of the view, so if it was rectangle CGRectMake(0,0,320,460) make it CGRectMake(0,0,320,480)... This has happened to me, Im n ot exactly sure why, it might ahve to do with the underlying view you are putting your text view on top off, but just adding the dimension should do it

Cable answered 10/7, 2009 at 15:29 Comment(1)
this did not have any effect.Foredate

© 2022 - 2024 — McMap. All rights reserved.