38

In a navigation controller, you automatically get the correct colour and position of a navigation bar as expected.

like this

enter image description here

But in modal view, when you drag in a navigation bar, you can position it right at the top, which is too close to the carrier / battery info.

enter image description here

So you can drag it down, guess how far so it matches the position of the automatically created one, but then you have a colour discrepancy. I have tried changing status bar settings in IB, doesnt make a difference.

enter image description here

Is there a correct way to do overcome this, as in make a modal view look like the auto generated nav view.

Thanks

Imre Kelényi
  • 22,113
  • 5
  • 35
  • 45
DogCoffee
  • 19,820
  • 10
  • 87
  • 120

9 Answers9

81

The best way to overcome this in iOS 7 is by conforming to the new UIBarPositioningDelegate protocol.

You connect the delegate of your NavigationBar to your view controller (set your view controller as the delegate for the navigation bar either through storyboard or through code) and conform to that protocol and by implementing the method

-(UIBarPosition)positionForBar:(id<UIBarPositioning>)bar { return UIBarPositionTopAttached; }

You can remove the top gap in the view controller. You need to place the bar 20 points below the top edge

Matt
  • 1,599
  • 14
  • 14
  • Thanks @CharlieSeligman but seeing that my answer isn't very old, people might have seen it enough especially since it has not been marked as the right one. – Matt Mar 19 '14 at 07:13
  • @Matt saved me a few grey hairs! Thank's a lot. – Anton Mar 27 '14 at 19:58
  • @Matt, good stuff! I changed my answer to yours, this is a clean approach. – DogCoffee Apr 01 '14 at 14:00
  • @Smick I'm happy to have helped. It took me a lot of digging when the iOS7 was launched. – Matt Apr 02 '14 at 12:38
  • What do you mean by "You connect the delegate of your NavigationBar to your view controller"? myNavBar.delegate = self doesn't work if you've only conformed to UIBarPositioningDelegate in your ViewController. – Joe C Apr 24 '15 at 17:24
  • I have done it both ways. Place the navBar in Storyboard 20 points below and control drag the delegate to the view controller itself from within the storyboard. Or, you could make an outlet and do myNavBar.delegate = self from within the view controller and conform to UIBarPositioningDelegate and implement the method. Are you sure your outlet is connected to the property or if it is created by code you have placed it right? – Matt Apr 27 '15 at 05:52
  • Thanks so much for this. I used a constraint to lock the top of the nav bar to the top layout guide, instead of 20px from the top-level view. Both seem to work when the status bar gets bigger (CMD+Y in simulator). Any downsides to using the top layout guide instead? – Gary Johnson Apr 20 '16 at 12:50
  • @GaryJohnson when you add a constraint to the navigation bar placed 20pts below, the constraint you add will be 0pts to top layout guide. It won't be to the superview. So what you did is exactly how you're supposed to do it. As by default on iOS 8 and above the status bar is hidden for landscape phone. – Matt Apr 25 '16 at 05:31
  • This worked for me but just to be clear, and answer @Matt, you need both `UINavigationBarDelegate` and `UIBarPositioningDelegate` – rmp Oct 26 '16 at 21:38
  • @rmp That's what I meant by "You connect the delegate of your NavigationBar". The easiest way is through storyboard but you can of course do it in code. – Matt Nov 17 '16 at 14:12
26

Figured out the 3 options for solving this problem.

Option 1: Resize the Nav Bar

float currentVersion = 7.0;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= currentVersion) {
    // iOS 7
    self.navBar.frame = CGRectMake(self.navBar.frame.origin.x, self.navBar.frame.origin.y, self.navBar.frame.size.width, 64);
}

Option 2: Hide the Status Bar

For example, in the modal view where you want to hide the status bar

Add this method

- (BOOL)prefersStatusBarHidden
{
    return YES;
}

In viewDidLoad add

float currentVersion = 7.0;

if ([[[UIDevice currentDevice] systemVersion] floatValue] >= currentVersion) {
    [self prefersStatusBarHidden];
    [self performSelector:@selector(setNeedsStatusBarAppearanceUpdate)];
}
else {
    [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
}

Now, when you dismiss the modal view, and you want your status bar back. Add this in viewWillAppear

    float currentVersion = 7.0;

if ([[[UIDevice currentDevice] systemVersion] floatValue] >= currentVersion) {
    [self prefersStatusBarHidden];
    [self performSelector:@selector(setNeedsStatusBarAppearanceUpdate)];
    NSLog(@"ios7");
}
else {
    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
}

and this, but return NO this time

- (BOOL)prefersStatusBarHidden
{
    return NO;
}

Option 3: Embed in Nav Controller

Select your modal view, just embed that in a Navigation Controller.

enter image description here

DogCoffee
  • 19,820
  • 10
  • 87
  • 120
  • I was having the same problem and I used Option 1 from your answer It works great. Have you found another way of some how setting the size of the navbar maybe in interface builder? I was just wondering if embedding something in navigation controller automatically generates that bar with the correct then there might be a way of doing it instead of resizing it in each view controller. – Naveed Oct 06 '13 at 19:28
  • @Naveed, havent found a way in IB. I didn't look at your second idea yet. – DogCoffee Oct 07 '13 at 00:54
  • I think all this is a bit complicated, just give a shot at what I posted below and give me a feedback if you can. Thanks anyway for your workaround ideas – Dulgan Nov 29 '13 at 08:57
  • Your third solution is working out wonders for me. It solved me two problems 1 - not having the title button close to the battery 2 - Title doesn't disappear when I scroll down the table, I need to do more work otherwise for a standalone navigation bar not embedded in a nav controller. – sysuser Jun 25 '15 at 06:57
  • Assigning to navigationBar.frame has no effect. – kelin Aug 18 '15 at 07:36
  • I tried option 3 and it worked fine. Option 3 seemingly is a clean option as it avoids writing additional code. – Sahil Khanna Apr 05 '16 at 06:40
4

In Swift:

The best way to overcome this in iOS 8.1 and Swift is by conforming to the new UIBarPositioningDelegate protocol.

You connect the delegate of your NavigationBar to your view controller and conform to that protocol and by calling the method:

func positionForBar(bar: UIBarPositioning) -> UIBarPosition  {
    return UIBarPosition.TopAttached
}

You can remove the top gap in the view controller. You need to place the bar 20 points below the top edge.

King-Wizard
  • 15,628
  • 6
  • 82
  • 76
  • 4
    Can you give an example detailing how to declare and assign the delegate? I'm doing `class ComposeViewController: UIViewController, UIBarPositioningDelegate` and then `self.navigationController?.navigationBar.delegate = self`. This doesn't compile since `UIBarPositioningDelegate` does not conform to `UINavigationBarDelegate`. – sleep Apr 07 '16 at 08:41
2

For Swift3 use following..

func position(for bar: UIBarPositioning) -> UIBarPosition{
    return .topAttached;
}
Susobhan Das
  • 125
  • 1
  • 3
0

After a few tries to move Navigation Bar few pixels down in iOS 7, this is what finally worked for me:

-(void)viewWillLayoutSubviews
{
    float iosVersion = 7.0;
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= iosVersion) {
        // iOS 7+
        CGRect viewFrame = self.view.frame;
        viewFrame.origin.y += 10;
        self.view.frame = viewFrame;
    }
}

I also adjusted the Status Bar color to better match my content:

-(UIStatusBarStyle)preferredStatusBarStyle{
    return UIStatusBarStyleLightContent;
}
Derek Gogol
  • 1,292
  • 16
  • 15
0

I created a Navigation Controller, deleted the "Root View Controller" that appeared with it. Then hold ctrl and drag the "Navigation Controller" on your View (delete the navigation bar you added manually before). Set the Navigation Controller as "Initial View Controller" and it works fine for me now.

0

isTranslucent is set by default to true. Setting it to false will extend the navigation bar below the status bar.

navigationBar.isTranslucent = false

Dejan Atanasov
  • 1,202
  • 10
  • 13
0

Other option, if you need navigation bar inside navigation controller, set your view controller background color to the same color as your navigation bar. Check that view's top constraint is higher than safe area top layout guide.

Oleg Kovtun
  • 362
  • 1
  • 3
-7

I turned "Use Autolayout" off and it worked for me.

  • 4
    Using Autolayout isn't the problem at all here. I think people want to use Autolayout AND solve this issue, so sorry for downvote but your answer is not useful – Dulgan Nov 29 '13 at 08:50