[ios] Status bar and navigation bar appear over my view's bounds in iOS 7

I recently downloaded Xcode 5 DP to test my apps in iOS 7. The first thing I noticed and confirmed is that my view's bounds is not always resized to account for the status bar and navigation bar.

In viewDidLayoutSubviews, I print the view's bounds:

{{0, 0}, {320, 568}}

This results in my content appearing below the navigation bar and status bar.

I know I could account for the height myself by getting the main screen's height, subtracting the status bar's height and navigation bar's height, but that seems like unnecessary extra work.

How can I fix this issue?

Update:

I've found a solution for this specific problem. Set the navigation bar's translucent property to NO:

self.navigationController.navigationBar.translucent = NO;

This will fix the view from being framed underneath the navigation bar and status bar.

However, I have not found a fix for the case when you want the navigation bar to be translucent. For instance, viewing a photo full screen, I wish to have the navigation bar translucent, and the view to be framed underneath it. That works, but when I toggle showing/hiding the navigation bar, I've experienced even stranger results. The first subview (a UIScrollView) gets its bounds y origin changed every time.

This question is related to ios ios7

The answer is


In my case having loadView() interrupted
this code: self.edgesForExtendedLayout = UIRectEdgeNone

but after deleting loadView() everything worked fine


I have a scenario where I use the BannerViewController written by Apple to display my ads and a ScrollViewController embedded in the BannerViewController.

To prevent the navigation bar from hiding my content, I had to make two changes.

1) Modify BannerViewController.m

- (void)viewDidLoad
{
   [super viewDidLoad];
   float systemVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
   if (systemVersion >= 7.0) {
      self.edgesForExtendedLayout = UIRectEdgeNone;
   }
}

2) Modify my ScrollViewContoller

- (void)viewDidLoad
{
    [super viewDidLoad];
    float systemVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
    if (systemVersion >= 7.0) {
        self.edgesForExtendedLayout = UIRectEdgeBottom;
    }
}

Now the ads show up correctly at the bottom of the view instead of being covered by the Navigation bar and the content on the top is not cut off.


Swift 3

override func viewWillAppear(_ animated: Bool) {
    self.edgesForExtendedLayout = []
}

You don't have to calculate how far to shift everything down, there's a build in property for this. In Interface Builder, highlight your view controller, and then navigate to the attributes inspector. Here you'll see some check boxes next to the words "Extend Edges". As you can see, in the first screenshot, the default selection is for content to appear under top and bottom bars, but not under opaque bars, which is why setting the bar style to not translucent worked for you.

As you can somewhat see in the first screenshot, there are two UI elements hiding below the navigation bar. (I've enabled wireframes in IB to illustrate this) These elements, a UIButton and a UISegmentedControl both have their "y" origin set to zero, and the view controller is set to allow content below the top bar.

enter image description here

This second screenshot shows what happens when you deselect the "Under Top Bars" check box. As you can see, the view controllers view has been shifted down appropriately for its y origin to be right underneath the navigation bar.

enter image description here

This can also be accomplished programmatically through the usage of -[UIViewController edgesForExtendedLayout]. Here's a link to the class reference for edgeForExtendedLayout, and for UIRectEdge

[self setEdgesForExtendedLayout:UIRectEdgeNone];

The simplest trick is to open the NIB file and do these two simple steps:

  1. Just toggle that and set it to the one you prefer:

Enter image description here

  1. Select those UIView's/UIIMageView's/... that you want to be moved down. In my case only the logo was overlapped an I've set the delta to +15; (OR -15 if you chose iOS 7 in step 1)

Enter image description here

And the result:

Before After


edgesForExtendedLayout does the trick for iOS 7. However, if you build the app across iOS 7 SDK and deploy it in iOS 6, the navigation bar appears translucent and the views go beneath it. So, to fix it for both iOS 7 as well as for iOS 6 do this:

self.navigationController.navigationBar.barStyle = UIBarStyleBlackOpaque;
if ([self respondsToSelector:@selector(edgesForExtendedLayout)])
    self.edgesForExtendedLayout = UIRectEdgeNone;   // iOS 7 specific

Steps For Hide the status bar in iOS 7:

1.Go to your application info.plist file.

2.And Set, View controller-based status bar appearance : Boolean NO

Hope i solved the status bar issue.....


Swift 4.2 - Xcode 10.0 - iOS 12.0:

if #available(iOS 11.0, *) {} else {
  self.edgesForExtendedLayout = []
  self.navigationController?.view.backgroundColor = .white
}

Swift Solution:

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    self.edgesForExtendedLayout = UIRectEdge.None
}

Add the key "View Controller-based status bar appearance" from the dropdownlist as a row in info.plist. Something like this:

Enter image description here


I created my view programmatically and this ended up working for me:

- (void) viewDidLayoutSubviews {
    // only works for iOS 7+
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
        CGRect viewBounds = self.view.bounds;
        CGFloat topBarOffset = self.topLayoutGuide.length;

        // snaps the view under the status bar (iOS 6 style)
        viewBounds.origin.y = topBarOffset * -1;

        // shrink the bounds of your view to compensate for the offset
        viewBounds.size.height = viewBounds.size.height + (topBarOffset * -1);
        self.view.bounds = viewBounds;
    }
}

Source (in topLayoutGuide section at bottom of pg.39).


Swift 3 / Swift 4 solution that also works with NIBs/XIB files in iOS 10+:

override func viewDidLoad() {
    super.viewDidLoad()

    edgesForExtendedLayout = []
}

just set the following code in view will appear.

  if ([[[UIDevice currentDevice] systemVersion] floatValue]<= 7) {
self.edgesForExtendedLayout = UIRectEdgeNone;
 }

To me, the simplest solution is to add two keys into the plist

enter image description here


If you want the view to have the translucent nav bar (which is kind of nice) you have to setup a contentInset or similar.

Here is how I do it:

// Check if we are running on ios7
if([[[[UIDevice currentDevice] systemVersion] componentsSeparatedByString:@"."][0] intValue] >= 7) {
      CGRect statusBarViewRect = [[UIApplication sharedApplication] statusBarFrame];
      float heightPadding = statusBarViewRect.size.height+self.navigationController.navigationBar.frame.size.height;

      myContentView.contentInset = UIEdgeInsetsMake(heightPadding, 0.0, 0.0, 0.0);
}

make a constraints to Top Layout like this enter image description here


I had the same issue with my app by iPads (armv7, armv7s, amr64) only by presenting another UIViewController and after dismiss them goes nav bar under status bar... I spend a lot of time to find any solution for that. I'm using storyboard and in InterfaceBuilder for UIViewController which makes terrible i set Presentation from FullScreen -> Current Context and it fix this issue. It works in my app only for iPads => iOS8.0 (testing with iOS8.1) and for iPads with iOS 7.1 not work!! see screenshot


In your apps plist file add a row, call it "View controller-based status bar appearance" and set it to NO.


I would like to expand on Stunner's answer, and add an if statement to check if it is iOS-7, because when I tested it on iOS 6 my app would crash.

The addition would be adding:

if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0)

So I would suggest adding this method to your MyViewControler.m file:

- (void) viewDidLayoutSubviews {
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
        CGRect viewBounds = self.view.bounds;
        CGFloat topBarOffset = self.topLayoutGuide.length;
        viewBounds.origin.y = topBarOffset * -1;
        self.view.bounds = viewBounds;
    }
}