48

Final EDIT

(Rather than having an overly long question with edits making a final edit for clarification, please see other edits if needed).

Controller Setup

I have an application that is setup as follows:

InitialViewController (subclass of ECSlidingViewController)

Main Navigation Controller (subclass of UINavigationController)

Main Home View Controller (subclass of UIViewController)


In the viewDidLoad of the initialViewController I load the main navigation controller in with the Home View Controller as its root.

self.topViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"MainNavVC"];

The Issue

On the first load of the application the status bar and navigation bar are seperated. enter image description here

This is the desired effect.

However, I then load a modal view controller and close it, using the standard methods:

[self performSegueWithIdentifier:@"LoadSelectOpponentVC" sender:self];

Then close with:

[self dismissViewControllerAnimated:YES completion:nil];

This in turn causes the main navigation controller (holding the home view controller) to display the status bar incorrectly and overlapping:

enter image description here

Testing

  1. The plist setting is set to YES - View controller-based status bar appearance
  2. I have tried setting the edgesForExtendedLayout to the relevant none, but no change.

Logging

I have tried to log out some frames to see where the issue occurs:

On first Load:

Main Nav VC - View Frame - {{0, 0}, {320, 480}}

Main Nav VC - Nav Bar Frame - {{0, 0}, {320, 44}}

Initial VC - View Frame - {{0, 0}, {320, 480}}

Home VC - View Frame - {{0, 0}, {320, 480}} -- viewDidLoad Home VC

Home VC - View Frame - {{0, 64}, {320, 416}} -- viewWillAppear Home VC

--- After Modal is opened/closed ----

Home VC - View Frame - {{0, 64}, {320, 416}} -- viewWillAppear Home VC

Main Nav VC - View Frame - {{0, 0}, {320, 480}} -- viewWillAppear Main Nav

Main Nav VC - Nav Bar Frame - {{0, 20}, {320, 44}} -- viewWillAppear Main Nav

Home VC - View Frame - {{0, 44}, {320, 436}} -- viewDidAppear Home VC
Community
  • 1
  • 1
StuartM
  • 6,743
  • 18
  • 84
  • 160
  • "Extend Under Top Bars" should only affect your view's relationship with the Navigation bar. – Greg M. Krsak Sep 23 '13 at 17:33
  • Have you tried moving the navigation bar's frame down by the height of the status bar? – erdekhayser Sep 27 '13 at 00:55
  • Where should this be done though? Can you supply as an answer please? – StuartM Sep 27 '13 at 10:24
  • Same issue overhere...looking for solution. – Jayprakash Dubey Oct 21 '13 at 14:30
  • Did you find any solution? Same problem for me , when I print navigationBar frame for first controller it prints (x,y)->(0,20) then when I push a view controller it prints (x,y)->(0,0). How did it become 0? – user1010819 Nov 05 '13 at 14:25
  • @mattsven - No we had to take a different approach due to time constraints. We left the SO question open as all the answers are relevant and workarounds to the issue. Thanks – StuartM Apr 07 '14 at 15:54
  • @StuartM Yeah, the fix was actually really simple, involved manipulating centerY - `navBar.centerY += 20`, when fading out the status bar, and that's it. – mattsven Apr 07 '14 at 19:50

14 Answers14

40

Did you try Apple recommendation about "Preventing the Status Bar from Covering Your Views": https://developer.apple.com/library/content/qa/qa1797/_index.html

And did you have a look at "UIBarPositioningDelegate": https://developer.apple.com/documentation/uikit/uibarpositioningdelegate

Cœur
  • 37,241
  • 25
  • 195
  • 267
Nicolas Buquet
  • 3,880
  • 28
  • 28
  • 8
    This reference says 'Applications that use an opaque UINavigationController or UITabBarController automatically keep their content below the status bar.' but this is clearly not true by my example/question. I have also tried to set the bar positioning as top and topattached to no avail. – StuartM Sep 26 '13 at 19:17
  • I used the method covered in the "Preventing the Status Bar from Covering Your Views" that sets up a vertical space constraint to the top layout guide and the UINavigationBar that I put on my view is placed below the status bar. I got the same result by pinning the UINavigationBar with 20 pixels of vertical space from the top of the view. – Moebius Sep 30 '13 at 03:52
  • make sure you set up AutoLayout correctly for the whole viewcontroller. The apple doc just says fix the first one. I only applied constraint between Top Layout Guide and top view and no other and it didnt work. But worked once Id added constraints to all view below viewcontroller.view at least and cleared all layout warning in IB – brian.clear Apr 23 '14 at 15:01
  • 3
    UIBarPositioningDelegate WORKS LIKE A CHARM! Just make your view controller a UIBarPositioningDelegate and assign it as the bar's delegate. Then add this method to your view controller (only available in iOS7+): - (UIBarPosition)positionForBar:(id)bar { return UIBarPositionTopAttached; } – jbcaveman May 23 '14 at 14:13
  • @JoelCave How do I `assign it as the bar's delegate`. Does the status bar have a delegate? or do you mean the UINavigationBar? – Daniel Sep 30 '15 at 15:27
  • Your view controller must be the delegate of the bar you are trying to position with "positionForBar:", not the status bar. – jbcaveman Oct 12 '15 at 20:04
26

In iOS 7.0 UIViewController works by default this way. View will be full screen if you are using UIViewController inside UINavigationController and the navigationBar is visible.

If navigationBar is visible do following. ==>

self.edgesForExtendedLayout = UIRectEdgeNone

if navigationBar is hidden do following . ==>

Adjust all the UIView elements by shifting 20 points

If you use Interface builder, you can use iOS6/7 deltas: First, "view as iOS 6.0", then set a delta of "20" to achieve the +20 offset in iOS 7

Sunil Adhyaru
  • 581
  • 6
  • 18
  • The edgesForExtendedLayout affects how the view controller is actually displayed with the status bar, wether the content will go underneath etc. I have put in my question that I had already tried this too. – StuartM Sep 26 '13 at 19:12
  • @SunilAdhyaru "First, "view as iOS 6.0", then set a delta of "20" to achieve the +20 offset in iOS 7" Can you explain this ? – onmyway133 Nov 24 '13 at 08:17
  • Sure. Basically you have to set view as a iOS 6.0 instead of 7.0. You can see it in "Interface Builder Document" item of Interface Builder. – Sunil Adhyaru Nov 24 '13 at 12:42
19

I'm surprised no one has hit on the right answer yet. UIBarPositioningDelegate works like a charm! Just make your view controller a UIBarPositioningDelegate and assign it as the bar's delegate. Position the bar 20 pixels from the top of your view. Then add this method to your view controller (only available in iOS7+):

- (UIBarPosition)positionForBar:(id<UIBarPositioning>)bar 
{ 
   return UIBarPositionTopAttached; 
}
jbcaveman
  • 911
  • 1
  • 10
  • 19
  • 1
    No we had to take a different approach due to time constraints. We left the SO question open as all the answers are relevant and workarounds to the issue. Thanks – StuartM May 27 '14 at 16:16
  • Can you elaborate on this? AFAIK you can't set the delegate of a navigation bar when it's managed by a navigation controller. – Alexis Jun 05 '14 at 09:28
  • 5
    This solution works when adding a custom navigation bar to a UIViewController. For a UINavigationController, you will need to create a subclass of UINavigationController and define that subclass as a UINavigationBarDelegate (which conforms to UIBarPositioningDelegate), similar to this: http://stackoverflow.com/a/7453933/485895. Just adding the positionForBar: method mentioned in my answer is all you need to do to solve the problem in question, but you can see that this subclass approach offers you other opportunities as well. – jbcaveman Jun 05 '14 at 13:51
4

I had a similar issue with a "hamburger" menu button that slide the main view controller over and had a menu view controller on the side. I found that the menu view controller's navigation bar was not aware if the status bar was shown or not. I fixed it by posting a notification when the status bar was shown and hiddden then doing

[self.navigationController setNavigationBarHidden:YES/NO animated:NO];

in the menu's view controller.

Stephen Johnson
  • 5,293
  • 1
  • 23
  • 37
3

Did you try to add the following code to your viewDidLoad method :

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

It is quickly explained in Apple migrating to iOS 7 doumentation.

Rishil Patel
  • 1,977
  • 3
  • 14
  • 30
Nicolas Buquet
  • 3,880
  • 28
  • 28
3

I fixed such a problem using answer from this post: iOS 7 | Navigation bar / Toolbar buttons very close to status bar

Using Autolayout you should ignore setting a new Frame. You should add a Top Space Constraint equal to 20 for the TopBar for iOS 7.

Community
  • 1
  • 1
grigorievs
  • 162
  • 6
  • I have tried the resolution to this question, but had no luck. Can you provide a code example of how you set out the AutoLayout, or did you compete in IB? – StuartM Oct 01 '13 at 16:45
  • 1. Add outlet for Bar Top Space Constraint (Vertical Space - Navigation Bar - View) in the code. 2. Set delegate for toolbar to the according VC. 3. Add de delegate method as specified in the url above. 4. Add this one in viewWillAppear > if (IS_IOS_7) self.navBarTopSpaceConstraint.constant = 20; – grigorievs Oct 02 '13 at 07:41
  • I have not yet been able to test this, I will get round to this soon and update – StuartM Oct 03 '13 at 17:46
  • Why you are asking people to help you if you aren't able to test(appreciate) someone's help? – grigorievs Oct 20 '13 at 12:11
  • I had to use a different approach as I was unable to get this resolved, I will re-open the project later today and try to get this to work. I beleive the issue lies with how the third party slide menu works as this was also reported to them and they were unable to resolve. But the question does provide a vast variety of information for other people with this issue to help try and resolve for them. – StuartM Oct 21 '13 at 07:59
1

I have answered this problem at length in this answer to a similar question. The short answer is this: there is no way to get the automatic status bar layout behavior you're used to from iOS 6 and earlier. You'll have to design around it, or find a way to simulate the old style (I cover both approaches).

I strongly advise you not to make manual adjustments to the navigation bar frame. Let UINavigationController handle that yourself. Most likely, your problem is that that your navigation controller's view's frame isn't equal to the UIScreen's bounds.

Community
  • 1
  • 1
jaredsinclair
  • 12,687
  • 5
  • 35
  • 56
  • Thanks, I have updated the question with a further EDIT. What I am finding is the frame changes for the navbar. Initially it loads as 0,20 offset and then later changes to be 0,0. When checking new apps on the app store they have the layout as 0,20 through out. Is there no easy way to accomplish this. Ideally I do not want to have to animate the navbar/other views down each time it is out of line – StuartM Sep 23 '13 at 18:12
  • Are you hiding the status bar while your modal view is visible? – jaredsinclair Sep 23 '13 at 18:14
  • What is the initial frame of your UINavigationController's view (**not** the navigation bar) in the NIB? – jaredsinclair Sep 23 '13 at 18:15
  • Are you using UINavgationBar directly, either in code or in your NIB? If so, why? – jaredsinclair Sep 23 '13 at 18:16
  • The navigation controllers view frame remains the same through out. nav view frame: {{0, 0}, {320, 480}}. I am not using navigation bar directly it is used from a navigation controller – StuartM Sep 23 '13 at 20:44
  • I do not think your answer is the solution here. It appears my navigation bar frame is being changed somehow but I cannot tell how/why. Your answer says that you cannot get the same affect as iOS6. What I am trying to achieve is the status bar as an item so that the navigation bar appears 20 down from the top and as one item together. Like most apps I see on the app store that have made themselves ios7 compat. – StuartM Sep 23 '13 at 22:28
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/37936/discussion-between-stuartm-and-jaredsinclair) – StuartM Sep 24 '13 at 11:23
  • Any thoughts following on from the above. I have commented with the relevant frame information. Your answer says that you cannot get the same affect as iOS6. What I am trying to achieve is the status bar as an item so that the navigation bar appears 20 down from the top and as one item together. Like most apps I see on the app store that have made themselves ios7 compat – StuartM Sep 24 '13 at 16:36
  • Not true. There is a way. See my answer below. You can keep this bar behavior with a UIBarPositioningDelegate. – jbcaveman May 23 '14 at 14:21
1

I know that you have ViewController as Main VC. But if someone is using UITableviewController and having the same problem, this code solves the issue:

self.tableView.contentInset = UIEdgeInsetsMake(20.0f, 0.0f, 0.0f, 0.0f);
itsji10dra
  • 4,603
  • 3
  • 39
  • 59
macL0vin
  • 183
  • 2
  • 11
0

The solution to this is actually very simple. It involves manipulating UINavigationBar's center.y value, which is what UIKit natively uses in order to adjust UINavigationBar to the status bar's height. For simplicity's sake, I subclassed UINavigationBar and did the following:

@implementation MyNavigationBar

    - (void) setCenter:(CGPoint)center {
        // Anything less than or equal to 22 is something we don't want (below SB height)
        if(center.y > 22) [super setCenter:center];
    }

@end
mattsven
  • 22,305
  • 11
  • 68
  • 104
0

Yes had same problem. followed all the step but no change.

Got it to work by making sure AutoLayout set up correctly for the whole screen not just the top view/toolbar as specified in

"Preventing the Status Bar from Covering Your Views" : https://developer.apple.com/library/ios/qa/qa1797/_index.html

At least for all Views just below the main Viewcontroller.view.

brian.clear
  • 5,277
  • 2
  • 41
  • 62
0

There is a built-in way to do this. Same as Joel Cave's answer, but elaborated:

Make your navigation bar have a Y origin of 20 points.

Then in the .h file:

@interface XYZViewController : UIViewController <UIBarPositioningDelegate>

And in the .m file:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationBar.delegate = self;
}

- (UIBarPosition)positionForBar:(id<UIBarPositioning>)bar { 
    return UIBarPositionTopAttached; 
}
Jeffrey Sun
  • 7,789
  • 1
  • 24
  • 17
  • My view controllers do not have a property called navigationBar. – Chris Prince Jul 06 '15 at 01:30
  • You can access it with this, but it doesn't solve the problem self.navigationController.navigationBar.delegate = self; If you success to solve the problem with this, tell me plz. It makes me crazy > – Verdant Jul 06 '15 at 20:30
  • Firstly the delegate type for the header is , secondly this doesn't solve the problem. – malhal Feb 28 '16 at 18:05
0

For me solution was be shifting Navigation Bar to 20 point enter image description here

dip
  • 129
  • 1
  • 4
-1

I'm a little too late to the party, but since I faced the same issue and this was the first result that showed up in the search, I guess my answer could help other people :)

I fixed the problem by implementing

- (BOOL)shouldAutorotate
{
    return NO;
}

in the view controller that presents the modal.

josemando
  • 573
  • 2
  • 9
-1

Try it, all navigationBars should be translucent is disable.

[self.navigationController.navigationBar setTranslucent:NO];

If you designed your view with a storyboard then you can solve the problem using XCode. Select the NavigationBar widget and uncheck "Translucent".

enter image description here

Nag Raja
  • 214
  • 2
  • 11