Status bar and navigation bar appear over my view's bounds in iOS 7
Asked Answered
M

20

444

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.

Maxwellmaxy answered 12/6, 2013 at 20:6 Comment(8)
I am also getting the same issue in xcode 5 DPAciculum
Could you tell me why navigation bar back button always showing with blue text???Aciculum
Look in the navigation bar for a tint color property, you should be able to change that blue color to whatever you want.Maxwellmaxy
I hate ios upgrading sometimes because Apple never gave you a opportunity to keep your app backwards compatible.Bernadettebernadina
@Maxwellmaxy put your update as an answer and accept that as the answer to help othersLeeds
If the problem is related to the view going under the status bar after hiding the navigation controller top bar I would refer to the answer by @Stunner https://mcmap.net/q/67071/-status-bar-and-navigation-bar-appear-over-my-view-39-s-bounds-in-ios-7 as the solutionAmmunition
Thanks for this, wish I would have found 8 hours ago... FWIW, a New install of my App does not have this problem, but upgrading my app does.. weird ios7 $#!+Abatement
doesnt when presenting modalviewcontrollersLeeds
C
498

You can achieve this by implementing a new property called edgesForExtendedLayout in iOS7 SDK. Please add the following code to achieve this,

if ([self respondsToSelector:@selector(edgesForExtendedLayout)])
        self.edgesForExtendedLayout = UIRectEdgeNone;

You need to add the above in your -(void)viewDidLoad method.

iOS 7 brings several changes to how you layout and customize the appearance of your UI. The changes in view-controller layout, tint color, and font affect all the UIKit objects in your app. In addition, enhancements to gesture recognizer APIs give you finer grained control over gesture interactions.

Using View Controllers

In iOS 7, view controllers use full-screen layout. At the same time, iOS 7 gives you more granular control over the way a view controller lays out its views. In particular, the concept of full-screen layout has been refined to let a view controller specify the layout of each edge of its view.

The wantsFullScreenLayout view controller property is deprecated in iOS 7. If you currently specify wantsFullScreenLayout = NO, the view controller may display its content at an unexpected screen location when it runs in iOS 7.

To adjust how a view controller lays out its views, UIViewController provides the following properties:

  • edgesForExtendedLayout

The edgesForExtendedLayout property uses the UIRectEdge type, which specifies each of a rectangle’s four edges, in addition to specifying none and all. Use edgesForExtendedLayout to specify which edges of a view should be extended, regardless of bar translucency. By default, the value of this property is UIRectEdgeAll.

  • extendedLayoutIncludesOpaqueBars

If your design uses opaque bars, refine edgesForExtendedLayout by also setting the extendedLayoutIncludesOpaqueBars property to NO. (The default value of extendedLayoutIncludesOpaqueBars is NO.)

  • automaticallyAdjustsScrollViewInsets

If you don’t want a scroll view’s content insets to be automatically adjusted, set automaticallyAdjustsScrollViewInsets to NO. (The default value of automaticallyAdjustsScrollViewInsets is YES.)

  • topLayoutGuide, bottomLayoutGuide

The topLayoutGuide and bottomLayoutGuide properties indicate the location of the top or bottom bar edges in a view controller’s view. If bars should overlap the top or bottom of a view, you can use Interface Builder to position the view relative to the bar by creating constraints to the bottom of topLayoutGuide or to the top of bottomLayoutGuide. (If no bars should overlap the view, the bottom of topLayoutGuide is the same as the top of the view and the top of bottomLayoutGuide is the same as the bottom of the view.) Both properties are lazily created when requested.

Please refer, apple doc

Cyclamate answered 13/9, 2013 at 11:52 Comment(14)
It will not compile under iOS6. I think it should be written as performselector...Tosspot
Yes, Thats why I have included the selector check with if condition, if ([self respondsToSelector:@selector(edgesForExtendedLayout)])Cyclamate
I tried it… But using this method changes all my bars from white to gray… Is there a solution?Seawright
@Julian set your window color to your view background color, i.e.: [self.window setBackgroundColor:[UIColor whiteColor]];Scabrous
This would not work if I have no navigation bar. In that case, my view is extends behind the status bar..Zooid
I have the same problem as @VanDuTran. edgesForExtendedLayout does not help if the navigation bar is hidden.Varicotomy
Also, this text is copied from the Apple documentation, which is very far from clear. Can somebody explain in understandable words what all these options do?Varicotomy
If the problem is related to the view going under the status bar after hiding the navigation controller top bar I would refer to the answer by @Stunner https://mcmap.net/q/67071/-status-bar-and-navigation-bar-appear-over-my-view-39-s-bounds-in-ios-7 as the solutionAmmunition
I did everything suggested: no change. The status bar is always shown translucent and also popup views show after the status barArraignment
I had an issue trying to use this solution. It was resolved by making sure I was in an Xcode 5.0 storyboard and had View as set to iOS 7.0 and later in the file inspector for the storyboard. If you have it set to an earlier version of Xcode, you won't get the new layout functionality.Uniformize
it works but ... there is no longer translucent effect... how can we preserve translucent effect as well...Equilibrate
adding this piece of code in -(void)viewDidLayoutSubviews{ did the trick for meSyman
Great answer! Btw, the code in swift is if(self.respondsToSelector(Selector("edgesForExtendedLayout"))) { self.edgesForExtendedLayout = UIRectEdge.None }Blackbeard
what about in iOS 8?Netsuke
F
112

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];
Flaminius answered 13/9, 2013 at 11:56 Comment(4)
How do I do the same for just view in a .xib?Digraph
I added one label on view and I followed your instruction but this is not working for meWarfore
@Digraph The answer to the XIB question is, "You can't." Not through IB anyway. File a radar and hope Apple eventually addresses it.Mcdowell
Thanks @0x7fffffff, This was just the info I was looking for and was very helpful.Baca
T
33

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).

Treasonous answered 24/9, 2013 at 8:35 Comment(12)
Finally found an answer that actually works for this. Good work!Deadwood
To solve the problem whereby the view controller's view shows up under the status bar when the navigation bar is hidden, this is the code which snaps the view under the status bar.Ammunition
Note: this will push the bottom of your view off the screen.Mcdowell
Also, unless you are building for only iOS7, the above code will throw an error - topLayoutGuide is iOS7 only.Mcdowell
Thank you, you just save me for 2 hours of plain GUI code nightmare! This work like magicMarotta
Put this as condition , so it will not crash on iOS 6.0 if ([self respondsToSelector:@selector(edgesForExtendedLayout)])Folacin
I have a issue with this code, I have table view inside the view and after applying this code table view goes beyond the bound.Pyromancy
As mentioned by AnswerBot, this code will push the bottom of your view offscreen. You'll need to compensate for that by modifying the height of your table view.Treasonous
Also note that when your main view is a UITableView the viewDidLayoutSubviews will get called on each scroll. Your UITableView will be scaled down until its height is 15px. Add a flag to only run this code once. ;)Julieannjulien
@Treasonous why don't you change the frame instead of bounds ?Neogene
You can change the frame as well and it will work. The reason I changed the bounds is because I ended up using the code snippet Apple provided, and that already used bounds, so I worked with that. See the source link I provided.Treasonous
Grate!!! it's working fine in both iOS6 & iOS 7 but I have seen my UIToolBar shirting bottom to top for with some color in iOS 7 for 2 seconds. any suggestions?? Thank youHypocycloid
B
32

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

override func viewDidLoad() {
    super.viewDidLoad()

    edgesForExtendedLayout = []
}
Buryat answered 1/9, 2016 at 20:17 Comment(3)
This is What Helped me... So Simple. Thanks :-)Clausen
Thank you it's really helpfulAnnabelannabela
This is the best solution for 10.x Swift 3+ IMOPeipus
A
10

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);
}
Alvardo answered 13/9, 2013 at 9:16 Comment(7)
why can't you directly compare it with 7.0Gastrolith
lol I get it: checking against a specific version with minor added is less robust. But you are checking if the value is BIGGER or equal to so comparing to 7.0 would do just fine here ;)Momism
@leena - This is features in 7.x and later - so I check if the version is 7 or greater and skip the minor revisions in the check.Alvardo
I Had an issue with my tableView beeing displayed behind my Tabbar. Which made the last row of my table never to scroll above the Tabbar. I fixed it like that: myTableView.contentInset = UIEdgeInsetsMake(0, 0.0, TabbarHeight, 0)Sphere
contentInset is a property of UIScrollView. What if my main view is not a subclass of UIScrollView. Then how would I fix the overlap problem?Mover
BOOL ios7 = [[UIDevice currentDevice] systemMajorVersion] == 7;Exhale
This is the best solution for my purpose. To make it better, collectionView.scrollIndicatorInsets should be set too. And translucent is meaningless under most circumstances if the view is not a scrollView.Dexedrine
C
9

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

Cloraclorinda answered 19/9, 2013 at 13:45 Comment(0)
S
9

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
Sandberg answered 21/9, 2013 at 5:18 Comment(0)
B
7

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

Become answered 20/9, 2013 at 10:27 Comment(2)
It works, but this look like a hard to maintain solution. It would be best to just fix the problem instead of patching it.Hippocampus
it would be best to do it programmatically for all view's subviews because it doesn't work if applied to main viewDisparagement
I
5

Swift Solution:

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    self.edgesForExtendedLayout = UIRectEdge.None
}
Instrumentalist answered 21/7, 2015 at 17:6 Comment(2)
This is now edgesForExtendedLayout = []Illstarred
Since this is a public answer we shouldn't forget to call: super.viewWillAppear(animated)Acetic
T
4

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;
    }
}
Tennison answered 1/10, 2013 at 15:36 Comment(0)
Y
4

Swift 3

override func viewWillAppear(_ animated: Bool) {
    self.edgesForExtendedLayout = []
}
Yung answered 4/7, 2017 at 7:27 Comment(0)
W
3

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.

Wallace answered 21/10, 2013 at 16:35 Comment(0)
M
2

just set the following code in view will appear.

  if ([[[UIDevice currentDevice] systemVersion] floatValue]<= 7) {
self.edgesForExtendedLayout = UIRectEdgeNone;
 }
Martinmartina answered 30/6, 2015 at 17:57 Comment(1)
This is the only thing that worked in my circumstance.Negotiant
H
2

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

Henrieta answered 10/9, 2015 at 9:0 Comment(0)
T
2

Swift 4.2 - Xcode 10.0 - iOS 12.0:

if #available(iOS 11.0, *) {} else {
  self.edgesForExtendedLayout = []
  self.navigationController?.view.backgroundColor = .white
}
Tannenbaum answered 25/8, 2018 at 12:18 Comment(0)
M
1

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

Mispronounce answered 23/9, 2013 at 21:17 Comment(1)
interesting.it helped on my cocos2d projects.as I toggle this option I can see the top status bar is completely has gone or appear.Mispronounce
A
1

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

enter image description here

Arraignment answered 3/10, 2013 at 10:28 Comment(1)
+1, I had the "Status bar initially hidden"=YES, but when adding "View controller-based status bar appearance"=NO I got rid of the status bar.Valenzuela
T
1

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

Tamper answered 30/1, 2015 at 9:52 Comment(1)
In my case, my main view was overlapping my tabBar and I had no Idea why. It turns out the Extend Edges > Under Bottom Bars was checked. It was really hard to notice it because the background of my app was white and it only generated some level opacity over my tabBar. Thanks!Feast
P
0

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.....

Pyxis answered 30/1, 2014 at 11:46 Comment(0)
V
0

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

but after deleting loadView() everything worked fine

Vaporish answered 1/3, 2017 at 5:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.