13
votes

I'm trying to display a UILabel on top of a UINavigationController. The problem is that when I add the UILabel as a subview of UIWindow it will not automatically rotate since it is not a subview of UIViewController (UIViewController automatically handles updating subviews during rotations).

This is the hierarchy I was using:

  • UIWindow
    • UILabel
    • UINavigationController

So I was thinking I could use the following hierarchy:

  • UIWindow
    • UIViewController
      • UIView
        • UILabel
        • UINavigationController

This way the label could be displayed on top of the UINavigationController's bar while also automatically being rotated since it is a subview of UIViewController.

The problem is that when I try adding a UINavigationController as a subview of a view:

[myViewController.view addSubview:myNavigationController.view];

it will appear 20 pixels downwards. Which I'm guessing is because it thinks it needs to make room for the status bar. But, since the UINavigationController is being placed inside a UIView which does not overlay on top of the status bar, it is incorrectly adding an additional 20 pixels. In other words, the top of the UINavigationBar is at the screen's 40 pixel mark instead of at 20 pixels.

Is there any easy way to just shift the UINavigationController and all of its elements (e.g. navigation bar, tool bar, root view controller) up 20 pixels? Or to let it know that it shouldn't compensate for a status bar?

If not, I guess I would need to use my first hierarchy mentioned above and figure out how to rotate the label so it is consistent with the navigation bar's rotation. Where can I find more information on how to do this?

Note: by "displaying a label on top of the navigation bar", I mean it should overlay on top of the navigation bar... it can't simply be wrapped in a bar button item and placed as one of the items of the navigation bar.

5

5 Answers

18
votes

Using this code seems to work:

nav.view.frame = CGRectMake(nav.view.frame.origin.x, nav.view.frame.origin.y - 20,
                          nav.view.frame.size.width, nav.view.frame.size.height);

I did this before adding the navigation controller as a subview. Using the [UIApplication sharedApplication].statusBarFrame instead of the hard coded 20 would probably be a good idea too.

I'm not sure if it's the best way to do it though.

2
votes

If you want a frame representing the available content area, then you should just use: [[UIScreen mainScreen] applicationFrame]. Of course, this restricts your top-level view controller so that it can only be top level. So still kind of dodgy, but less so.

2
votes

Why don't you use App Frame instead of adding some values to origins? I mean using:

CGRect appFrame = [[UIScreen mainScreen] applicationFrame];

as a reference frame, and do something like this:

nav.view.frame = CGRectMake(appFrame.origin.x, appFrame.origin.y, ...

This one worked for me.

0
votes

I had this same problem actually but managed to fix it.

I noticed that my view controller's view had the correct frame, but the view controller's navigation bar did not (it had a frame origin of (0,20) ).

Insert this into the view's controller that is the superview of the navigation controller:

- (void) viewDidAppear:(BOOL)animated {
    if (navigationController.navigationBar.frame.origin.y != 0) {
        [[navigationController view] removeFromSuperview];
        [[self view] addSubview:navigationController.view];
    }
}
0
votes

Swift 5: add the following line in the viewDidLoad() of the root view controller of the UINavigationController.

self.edgesForExtendedLayout = [.top, .bottom]