23

How can I make my view resize in response to the in-call status bar from my nib?

I figured it would just be setting the resize properties, but they're not enabled for the root UIView.

(I think my main problem here is I don't know what any of this is called; I can't find any reference to the in-call status bar in any of the documentation except where it talks about the simulator menu command.)

john.k.doe
  • 7,533
  • 2
  • 37
  • 64
Steven Fisher
  • 44,462
  • 20
  • 138
  • 192

11 Answers11

24

iOS will invoke your viewController's viewWillLayoutSubviews method whenever there is a change in status bar. You can override that and adjust your subviews according to the new bounds.

- (void)viewWillLayoutSubviews {
    // Your adjustments accd to 
    // viewController.bounds

    [super viewWillLayoutSubviews];
}
Santhos Ramalingam
  • 1,188
  • 10
  • 11
  • Well if you modify the bounds here it `viewWillLayoutSubviews` will be called again recursively. – Rivera Sep 02 '15 at 13:59
  • strange that viewController is notified but it does not get resized, height srinked, any idea why? – János Oct 05 '15 at 14:30
  • Great! You can update your contentFrame that you might use as a constant in your UIViewController here as well to self.view.bounds, so the offset propagates throughout your view hierarchy. – serge-k Feb 23 '16 at 19:10
16

You're looking for -[UIApplication statusBarFrame] and, in your UIApplicationDelegate, you should implement this delegate method to be notified of when the status bar's frame changes:

- (void)application:(UIApplication *)application didChangeStatusBarFrame:(CGRect)oldStatusBarFrame  
Mike McMaster
  • 7,573
  • 8
  • 37
  • 42
  • 1
    Mike, can you clarify: Does this mean I must manually resize the items on my views in code? Gasp! – Steven Fisher Jul 11 '09 at 20:43
  • 1
    I don't see why if your view's autoresizing properties are properly set. But yeah, writing code - what a horrible inconvenience. :-) – Mike McMaster Jul 11 '09 at 20:59
  • Marking the other accepted because he pointed out that what I was trying to do *should* have worked first, but thank you very much. :) – Steven Fisher Jul 11 '09 at 21:07
  • 4
    This method is not called when the status bar changes height as a result of ending a call. As far as I can tell, it is only called when rotating a view. – Jonah Apr 19 '10 at 23:17
7

A view may not resize automatically with status bar size changes if you do not have a root view controller set. I originally had this in my app delegate, and my app worked properly in all regards except that it would not resize correctly during phone calls.

[self.window addSubview:rootController.view];

I changed the above line to this, and now my app resizes automatically during calls.

[self.window setRootViewController:rootController];

I discovered this fix after seeing this in the log and investigating the cause.

Application windows are expected to have a root view controller at the end of application launch
Wade
  • 299
  • 5
  • 9
  • OMG this! This solved my problem, after spending hours tweaking the fullScreen properties of almost everything! – ArkReversed Jul 29 '14 at 11:32
7

this works for me perfectly when my application is running in background and I press command + T. In my scenario , the root controller is my tab bar controller and I am readjusting my nav controller inside each tab.

- (void)application:(UIApplication *)application willChangeStatusBarFrame:(CGRect)newStatusBarFrame{
[UIView animateWithDuration:0.35 animations:^{
    CGRect windowFrame = ((UINavigationController *)((UITabBarController *)self.window.rootViewController).viewControllers[0]).view.frame;
    if (newStatusBarFrame.size.height > 20) {
        windowFrame.origin.y = newStatusBarFrame.size.height - 20 ;// old status bar frame is 20
    }
    else{
        windowFrame.origin.y = 0.0;
    }
    ((UINavigationController *)((UITabBarController *)self.window.rootViewController).viewControllers[0]).view.frame = windowFrame;
}];

}

megha
  • 473
  • 5
  • 18
  • This shouldn't be necessary; I think there's a bug in Xcode's templates. I fixed it by deleting + recreating the view controller. The new one responds the way I expected. – Steven Fisher Jul 04 '13 at 18:10
  • I fixed it , in my case i had pushed a tab bar controller inside a navigation controller, so it was causing the issues. Now it works seamlessly. Now tab bar controller is my root controller and I am readjusting my navigation controller's view inside tab bar controller. – megha Jul 05 '13 at 10:55
6

What do you mean when you say that 'the resize properties aren't enabled for the root UIView'?

The in-call status bar doesn't have any particular special designation, and I don't think there are any APIs or notifications around it. Instead, your views should simply be set up to autoresize correctly.

Try creating a new navigation-based app in Xcode and study the autoresize settings on the table view in RootViewController.xib. Hopefully you'll see a delta between what Apple's set and what you've set in your project.

Aaron Brethorst
  • 763
  • 1
  • 6
  • 17
  • I'm probably using the wrong term there, too. Basically, I have a xib with a UIView. Selecting the UIView and looking at the resize properties, I can click the top/bottom and left/right connections, but the horizontal and vertical in the center don't respond to clicks. – Steven Fisher Jul 11 '09 at 21:00
  • Created a new UIView in the xib, and I can manipulate it. Seems to be something wrong with that UIView in the xib. Will experiment a bit more and mark this as the solution if so. – Steven Fisher Jul 11 '09 at 21:01
  • Yes, it looks like something's simply wrong with that UIView. Thanks. – Steven Fisher Jul 11 '09 at 21:06
  • Turn every Simulated Metrics off in your XIB if you need to change your view frame and autoresizingmask from IB: As soon as you set one simulated metric, like the status bar style, or simulating a bottom tab bar or a nav bar at the top will make IB disable the ability to change the View frame & ARM manually, especially because it calculates these according to these Simulated Metrics. – AliSoftware Jun 17 '11 at 21:36
  • Actually there is a notification indicating that the status bar will change: `UIApplicationWillChangeStatusBarFrameNotification`. And even with autolayout correctly setup, you may need to respond to the frame change. For example, a table view that calculates its cell height based on the given space in the screen would may need to `reloadData` after the status bar changed. – sabajt Jan 23 '15 at 16:03
3

For views like UITableView you'll typically need to change the table cell height, and there's no other way to do it except to implement application:didChangeStatusBarFrame:. But it's no biggie, and you can set the row height to non-integer values if you need to.

Scott Lahteine
  • 1,040
  • 11
  • 20
  • Except that application:didChangeStatusBarFrame: and the accompanying notification is never sent for a simple status frame change. Try it :-) – Steve Weller Dec 30 '09 at 01:04
  • I noticed that when I got around to trying it. So how do we handle the change in screen size? My app's interface slides down but gets partially obscured in the process. – Scott Lahteine Dec 30 '09 at 20:36
2

The notification for status bar changing frame is

UIApplicationWillChangeStatusBarFrameNotification

Register as an observer:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarFrameWillChange:)name:UIApplicationWillChangeStatusBarFrameNotification object:nil];

Then respond to change in your handler:

- (void)statusBarFrameWillChange:(NSNotification*)notification { // respond to changes }

Even with autolayout setup correctly, you may need to respond to changes. For example, a table view that calculates its cell height based on the given space in the screen would may need to reloadData after the status bar changed.

Documentation

sabajt
  • 426
  • 1
  • 6
  • 11
1

You will have to update the views manually if you are setting your view frames programatically

 -(void) viewDidAppear:(BOOL)animated
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(statusBarFrameWillChange:)name:UIApplicationWillChangeStatusBarFrameNotification object:nil];

}

- (void)statusBarFrameWillChange:(NSNotification*)notification
{
// respond to changes
NSLog(@"STATUS BAR UPDATED");
NSDictionary *statusBarDetail = [notification userInfo];
NSValue *animationCurve = statusBarDetail[UIApplicationStatusBarFrameUserInfoKey];
CGRect statusBarFrameBeginRect = [animationCurve CGRectValue];
int statusBarHeight = (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication]statusBarOrientation])) ? statusBarFrameBeginRect.size.height : statusBarFrameBeginRect.size.width;

NSLog(@"status bar height  %d", statusBarHeight);
}

-(void) viewDidDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationBackgroundRefreshStatusDidChangeNotification object:nil];
}

This gives you the new height of the status bar and using this you can update your frames accordingly.

genaks
  • 757
  • 2
  • 10
  • 24
1

I was having the same problem. What I did was this.

  1. Open the xib file in IB
  2. All interface elements are by default attached to move along with with top and shown in the 'Autosizing' property in the 'Size Inspector'. So, for the UI elements at the bottom of the screen, remove the link from top and instead make the link from bottom. Leave all the others as is.
  3. Problem Solved!!!

I hope I was clear.

Bushra Shahid
  • 3,579
  • 1
  • 27
  • 37
0

The solution is to ensure you have made your window key:

[window makeKeyAndVisible];

If you don't do that the subview does not get resized automatically, but there are no other symptoms as far as I know.

Steve Weller
  • 4,599
  • 4
  • 23
  • 30
0

None of the solutions posted before worked for me. What did work for me was that I didn't have my constraints set up properly for my views. In my situation, I had two different container views within my view.

View of scene in storyboard.

The bottom (in the list) container view was a UITableView, which was not scrolling correctly to the bottom of the table. The reason was that the offset of the in-call status bar was causing the origin (top-left corner) of the UITableView to change, but the size would remain this same. This meant that the bottom of the table was off screen!

The solution was to correctly set the Autoresizing height constraint properly. In the screenshot below, it is the vertical arrows in the middle of the Autoresizing box.

enter image description here

mikeho
  • 6,790
  • 3
  • 34
  • 46
  • My original problem, way back when, was that these arrows were simply unavailable. I never figured out why, but recreating the view within the view controller fixed it. – Steven Fisher Nov 26 '14 at 22:01