E-Madd
E-Madd

Reputation: 4582

UINavigationController within ViewController, gap at top of view

I'm working on a universal app, and I'm trying to share as much code as possible between the iPhone and iPad versions. I need to use a TabBarController as my root view controller and though I'd like to use a SplitViewController in each of the tabs, SplitViewController's docs say it needs to be the root view controller. So, with all of that in mind - understand that I'm attempting to place two navigation controllers side-by-side and (mostly) replicate the behavior/layout of a SplitViewController.

Everything works just fine, except for the layout of the views. When the app is started in portrait mode, everything functions and resizes/positions correctly when the device orientation changes.

portrait orientation

If the app is started in any orientation other than UIDeviceOrientationPortrait, the view displays with a 20 point gap/margin above the navigation controller. I've tried adjusting the frame at runtime with no perfect result. Adjusting the origin.y of the frame to -20 and increasing the height by 20 brings the view flush with the top of it's parent, but it leaves a 20 point gap at the bottom!

landscape orientation

Upvotes: 20

Views: 13811

Answers (11)

NoahSteam
NoahSteam

Reputation: 21

I ran into the same issue when adding a UITableView to my ViewController on viewDidLoad. Instead of grabbing the frame from self.view, I got it form the main window so that it looks like this

UIWindow *keyWindow = [[[UIApplication sharedApplication] delegate] window];
self.uiTable = [[UITableView alloc] initWithFrame:[keyWindow frame] style:UITableViewStylePlain];

Upvotes: 2

ant_one
ant_one

Reputation: 845

To fix this problem just check the box Wants Full Screen on the storyboard.

The problem appears because the ParentViewController is showing the navigation bar.

As apple documentation said :

If your app displays the status bar, the view shrinks so that it does not underlap the status bar. After all, if the status bar is opaque, there is no way to see or interact with the content lying underneath it. However, if your app displays a translucent status bar, you can set the value of your view controller’s wantsFullScreenLayout property to YES to allow your view to be displayed full screen. The status bar is drawn over the top of the view.

Upvotes: 3

J3RM
J3RM

Reputation: 1722

In my view that is displayed inside the navigationcontroller I put this code inside the viewDidAppear

This code may not be perfect for every application but it fixed my issue I spent several hours kicking at..

    // START OF BUG FIX FOR iOS
if (self.navigationController.navigationBar.frame.origin.y ==20) {
    // Move the navigation Bar up
    [self.navigationController.navigationBar setFrame:CGRectMake(0, 0, self.navigationController.navigationBar.frame.size.width, self.navigationController.navigationBar.frame.size.height)];
    // move up the table view
     [self.view setFrame:CGRectMake(0, -20, self.view.frame.size.width, self.view.frame.size.height+20)];
}
// END OF BUG FIX for IOS6

Upvotes: 0

Becca Royal-Gordon
Becca Royal-Gordon

Reputation: 17861

Oddly enough, what's helping for me in iOS 6 is subclassing UINavigationController and implementing this method:

- (BOOL)wantsFullScreenLayout {
    return NO;
}

Unchecking "Wants Full Screen" in my storyboard didn't do the trick, so I don't know what's different. This could certainly break in a future version of iOS. But for now, I'll take it.

Upvotes: 16

bertz
bertz

Reputation: 1

had a similar problem with the unwanted gap between the status bar and the view directly below it, solved it by going to autosizing in interface builder and setting the view in question to stick to the top of its parent view.

Upvotes: 0

kevin
kevin

Reputation: 2071

I solved this in my app by hiding then showing the navigation bar after adding the navigation controllers view. eg.

[parentView addSubview:navController.view];
[navController setNavigationBarHidden:YES];
[navController setNavigationBarHidden:NO];

Upvotes: 25

Michał Orlik
Michał Orlik

Reputation: 21

This is a common issue when adding view controllers to a tab bar.

From Apple's documentation (Tab Bar Controllers - Tab Bars and Full-Screen Layout):

Tab bar controllers support full-screen layout differently from the way most other controllers support it. You can still set the wantsFullScreenLayout property of your custom view controller to YES if you want its view to underlap the status bar or a navigation bar (if present). However, setting this property to YES does not cause the view to underlap the tab bar view. The tab bar controller always resizes your view to prevent it from underlapping the tab bar.

In words of code, you should do the following:

UINavigationController *myNavController = [[UINavigationView alloc] init];
myNavController.wantsFullScreenLayout = YES;

//...

NSArray* controllers = [NSArray arrayWithObjects:myNavController, nil];
myTabBarController.viewControllers = controllers;

If, however, you run into the problem that when opening application in orientation other than UIInterfaceOrientationPortrait your myNavController's view moves 20 pix off the screen to the top, then you will have set controller's wantsFullScreenLayout property dynamically (instead of the above solution), depending on the initial orientation. I do it using a static variable defined in your navigation controller implementation:

static UIInterfaceOrientation _initialOrientation = -1;

After that you need to overload the viewDidAppear: method and set the variable appropriately:

- (void)viewDidAppear:(BOOL)animated
{
  if (_initialOrientation == -1)
    _initialOrientation = [[UIApplication sharedApplication] statusBarOrientation];
  self.wantsFullScreenLayout = (_initialOrientation != UIInterfaceOrientationPortrait);

  [super viewDidAppear:animated];
}

Hope this helps.

Upvotes: 2

ynnckcmprnl
ynnckcmprnl

Reputation: 4352

Not sure whether it'll be of any help, but I've had similar issues in the past and have been able to resolve this by setting the frame of the UINavigationController view to CGRectZero when adding it to its superview.

In layoutSubviews I update the frame of the UINavigationController view of course.

Upvotes: 2

SmallChess
SmallChess

Reputation: 8127

The proper answer is:

CGRect r = [[_navController view] frame];
r.origin = CGPointMake(0.0f, -20.0f);
[[_navController view] setFrame:r];

Upvotes: -1

Frank Schmitt
Frank Schmitt

Reputation: 25775

I was able to brute force as follows. In viewDidLoad or loadView:

if (self.navigationController.navigationBar.frame.origin.y > 0.0) {
    self.navigationController.navigationBar.frame = CGRectOffset(self.navigationController.navigationBar.frame, 0.0, -20.0);
}

And in viewDidAppear:

if (self.view.superview.frame.origin.y > 44.0) {
    UIView *container = self.view.superview;
    container.frame = CGRectMake(container.frame.origin.x, container.frame.origin.y - 20.0, container.frame.size.width, container.frame.size.height + 20.0);
}

It's ugly, but it seems to work.

Upvotes: 3

Anomie
Anomie

Reputation: 94804

UINavigationController is normally displayed as a full-screen controller, which means that (when displayed as the root view controller of the window) the top part of its view is placed under the status bar. It then manually positions its navigation bar and content view to not underlap the status bar when the status bar is visible. Unfortunately, it doesn't really handle things correctly when it is being positioned by some other view controller, it (sometimes) assumes it needs to leave that 20px gap without checking whether its view actually is under the status bar.

The solution is to set wantsFullScreenLayout on the UINavigationController to NO, so it won't even attempt to leave that gap for the status bar.

Upvotes: 3

Related Questions