11

The iOS 7 Transition Guide give a good hint how to change the UIStatusBarStyle dynamically in a UIViewController using

- (UIStatusBarStyle)preferredStatusBarStyle {
     return UIStatusBarStyleDefault;
}

together with [self setNeedsStatusBarAppearanceUpdate];

This works fine in a single view application. However, I'm now trying to change the UIStatusBarStyle in a modal view to UIStatusBarStyleLightContent. There is a MainViewController which segues to the ModalViewController, which itself is embedded in a NavigationController. The ModalViewController has set its delegate to the MainViewController.

I tried to call [self setNeedsStatusBarAppearanceUpdate]; in the ModalViewController together with the following method in that class without effect:

// In ModalViewController.m
- (UIStatusBarStyle)preferredStatusBarStyle {
     return UIStatusBarStyleLightContent;
}

I also tried to call [self setNeedsStatusBarAppearanceUpdate]; in MainViewController on prepareForSegue: sender: method with conditions in - (UIStatusBarStyle)preferredStatusBarStyle {} to return UIStatusBarStyleLightContent when the modal view is presented - but that has no effects, too.

How can I change the UIStatusBarStyle in the modal view?

EDIT: Post updated: I need to mention that the ModalViewController is embedded in a NavigationController with a NavigationBar. With NavigationBar set to hidden to above call of [self setNeedsStatusBarAppearanceUpdate]; in ModalViewController works fine. But not when the Bar is visible.

FrankZp
  • 2,022
  • 3
  • 28
  • 41

9 Answers9

29

You need a ViewController that's showing in Fullscreen to return the appropriate status bar infos. In your case: The NavigationController which contains ModalViewController needs to implement preferredStatusBarStyle and return UIStatusBarStyleLightContent.

A call to setNeedsStatusBarAppearanceUpdate is only necessary if the values a view controller returns actually change. When the view controller is first presented they are queried anyway.

jaetzold
  • 1,638
  • 14
  • 10
  • You saved me a bunch of time right now. iOS 7 apps that also need to support iOS 6 are terrible to work with. Thanks a bunch! – bmeulmeester Nov 07 '13 at 09:13
  • 1
    How can I implement the preferredStatusBarStyle in my AppDelegate? I have a TabBarViewController leading to 4 NavControllers that lead to UIViewControllers, should I be trying this in a class for Tab Bar? in the Navcontrollers? or in AppDelegate? -I need all status bars set to LightContent – Chisx Jan 04 '14 at 03:33
  • In your case it's probably a subclass of TabBarViewController which has to implement `preferredStatusBarStyle` – jaetzold Jan 04 '14 at 09:42
  • `preferredStatusBarStyle` wasn't called for me because I used `UIModalPresentationOverFullScreen` instead of `UIModalPresentationOverCurrentContext`! – DanSkeel May 11 '16 at 18:47
25

We should notice that non-fullscreen modalVC CAN use modalPresentationCapturesStatusBarAppearance to control the statusBar style.

Anyone who wanna know more about Status Bar control should not ignore the UIViewController Managing the Status Bar.

Update at 2015-11-06:

And make sure you have set UIViewControllerBasedStatusBarAppearance described in iOS Keys

Update at 2018.04.09:

I noticed that viewController in a navController may not get call prefersStatusBarHidden with iOS 10.0 - 10.2. Custom your navigationController to ensure that

@implementation YourCustomNavController
//for iOS 10.0 - iOS 10.2
- (BOOL)prefersStatusBarHidden {
    UIViewController *childVC = [self childViewControllerForStatusBarHidden];
    if (childVC) {
        return [childVC prefersStatusBarHidden];
    }
    return [super prefersStatusBarHidden];
}
@end

And anyone who want to go deeper inside can dig into UIKit +[UIViewController _currentStatusBarStyleViewController] using Hopper or IDA Pro. It may helps you solve these kinds of bugs.

Puttin
  • 1,596
  • 23
  • 27
  • Perfect, this was exactly the issue that I was facing, that a custom presented VC was not changing the status bar appearance and the previous view was still being queried. – Guykun Jul 12 '16 at 01:51
11

The key to making this work is that only the fullscreen view controller get's to dictate the style of the status bar.

If you are using a navigation controller and want to control the status bar on a per view controller basis, you'll want to subclass UINavigationController and implement preferredStatusBarStyle such that it returns the topViewController's preference.

Make sure you change the class reference in your storyboard scene fromUINavigationController to your subclass (e.g. MyNavigationController in the example below).

(The following works for me. If your app is TabBar based, you'll want to do something similar by subclassing the UITabBarController but I haven't tried that out).

@interface MyNavigationController : UINavigationController

@end

@implementation MyNavigationController

- (UIStatusBarStyle)preferredStatusBarStyle
{
    return self.topViewController.preferredStatusBarStyle;
}

@end
Juddster
  • 189
  • 1
  • 4
  • Subclassing UINavigationController works great, thanks! I also did a quick experiment with a category. It actually works just as well as a subclass. – Lee Irvine Nov 01 '14 at 19:52
6

To change the status bar of the UINavigationController embedding your ViewController without subclassing UINavigationController, use this:

navigationController?.navigationBar.barStyle = .Black // to make the status bar text white

.Black will make the text white (status bar and the view's title), while .Default has a black title and status bar.

Alex Wally
  • 2,299
  • 1
  • 20
  • 18
2

I had a side menu/reveal controller (SWRevealController) which turns out to always be the root controller for status bar queries. Overriding childViewControllerForStatusBarStyle let me re-route the query to the front most controller.

/**
 This view is always considered the topmost for status bar color queries.
 Pass the query along to what we're showing in front.
 */
- (UIViewController *)childViewControllerForStatusBarStyle
{
    UIViewController *front = self.frontViewController;
    if ([front isKindOfClass:[UINavigationController class]])
        return ((UINavigationController*)front).topViewController;
    else
        return front;
}
Graham Perks
  • 23,007
  • 8
  • 61
  • 83
1

It seems like the app goes off the statusBarStyle of the topmost viewController. So if you add another viewController on top of your current one, it now gets its cues from the new viewController.

William Jockusch
  • 26,513
  • 49
  • 182
  • 323
1

This works for me:

  1. Set View controller-based status bar appearance to NO
  2. Set Status bar style to UIStatusBarStyleLightContent (just copy that value)
  3. In appDelegate use [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

Hope it helps (ref: ios7 status bar changing back to black on modal views?)

Community
  • 1
  • 1
samthui7
  • 923
  • 2
  • 12
  • 11
0

Just look up if your app's rootViewController need to override -(UIStatusBarStyle)preferredStatusBarStyle method

0

All of the above work. However sometimes I find it really a pain in the bottom to go and change every instance in the Storyboard etc... So here's something that works for me that also involves subclassing.

First create the subclass:

@interface HHNavLightColorBarController : UINavigationController

@end

@implementation HHNavLightColorBarController

- (UIStatusBarStyle)preferredStatusBarStyle {
    return UIStatusBarStyleLightContent;
}
@end

Then using the magic of Objective-C and a little bit of the <objc/runtime.h>

When you have a reference of the view controller and your presenting it:

UINavigationController *navVC = ...; // Init in your usual way
object_setClass(navVC, [HHNavLightColorBarController class]);
[self presentViewController:nav animated:YES completion:^{
    NSLog(@"Launch Modal View Controller");
}];

Sometimes it seems a bit less intrusive. You could probably even create a category that checks to see if your kindOfClass is a navigation controller and auto do it for you. Anyways, the answer is above by jaetzold, I just found this to be handy.

bainfu
  • 51
  • 1
  • 1