UIStatusBarStyle PreferredStatusBarStyle does not work on iOS 7

82,372

Solution 1

OK, here's the trick. You do have to add the key "View controller-based status bar" and set the value to No.

This is counter to what it appears the meaning of this key is, but even if you set the value to No, you can still change the appearance of the status bar, and whether it shows or not in any view controller. So it acts like "Yes" but set it to "No"!

Now I can get the status bar white or dark.

Solution 2

I discovered that if your ViewController is inside a navigationController then the navigationController’s navigationBar.barStyle determines the statusBarStyle.

Setting your navigationBar’s barStyle to UIBarStyleBlackTranslucent will give white status bar text (ie. UIStatusBarStyleLightContent), and UIBarStyleDefault will give black status bar text (ie. UIStatusBarStyleDefault).

Note that this applies even if you totally change the navigationBar’s color via its barTintColor.

Solution 3

For preferredStatusBarStyle() to work within UINavigationController and UITabBarController I add the following code, which will get the preferred status bar style from the currently visible view controller.

extension UITabBarController {
    public override func childViewControllerForStatusBarStyle() -> UIViewController? {
        return selectedViewController
    }
}

extension UINavigationController {
    public override func childViewControllerForStatusBarStyle() -> UIViewController? {
        return visibleViewController
    }
}

For Swift 3 those are not methods but properties:

extension UITabBarController {
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return selectedViewController
    }
}

extension UINavigationController {
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return visibleViewController
    }
}

The Swift 4.2 properties have been renamed:

extension UITabBarController {
   open override var childForStatusBarStyle: UIViewController? {
        return selectedViewController
    }
}

extension UINavigationController {
   open override var childForStatusBarStyle: UIViewController? {
        return visibleViewController
    }
}

Usage

class ViewController: UIViewController {

    // This will be called every time the ViewController appears
    // Works great for pushing & popping
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }

}

Solution 4

I may be coming to this a bit late, but incase anyone else is looking for a working and verified app wide solution.

@mxcl is correct in describing why this is happening. In order to correct it, we simply create an extension (or category in obj-c) that overrides the preferredSatusBarStyle() method of UINavigationController. Here is an example in Swift:

extension UINavigationController {
    public override func preferredStatusBarStyle() -> UIStatusBarStyle {
        if let rootViewController = self.viewControllers.first {
            return rootViewController.preferredStatusBarStyle()
        }
        return super.preferredStatusBarStyle()
    }
}

This code simply extracts the first view controller (the root view controller) and unwraps it (in obj-c just check that it is not nil). If the unwrap is successful (not nil) then we grab the rootViewControllers preferredStatusBarStyle. Otherwise we just return the default.

Hope this helps anyone who might need it.

Solution 5

To provide more detail into the accepted answer, put the following line in your app delegate's didFinishLaunchingWithOptions: method:

[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;

Then, in your Info.plist, add View controller-based status bar appearance and set it to NO.

I believe that's how it should be done, NOT from the navigation controller, if you want the same status bar color for the entire app. You might have screens that are not necessarily embedded in a UINavigationController, or a different UINavigationController subclass somewhere else, and other things.

EDIT: You can also do it without typing any code: https://stackoverflow.com/a/18732865/855680

Share:
82,372

Related videos on Youtube

Andrew Smith
Author by

Andrew Smith

I've been writing audio test and measurement apps on iOS for 7 years now, and I'm still learning every day.

Updated on May 13, 2022

Comments

  • Andrew Smith
    Andrew Smith about 2 years

    In my iPhone application built with Xcode 5 for iOS 7 I set UIViewControllerBasedStatusBarAppearance=YES in info.plist, and in my ViewController I have this code:

    -(UIStatusBarStyle) preferredStatusBarStyle
    {
        return UIStatusBarStyleLightContent;
    }
    

    But the status bar is still black against the black background.

    I know its possible to change this app-wide by setting UIViewControllerBasedStatusBarAppearance=NO in info.plist, but I actually need to alter this on a viewController by viewController basis at runtime.

    • Noundla Sandeep
      Noundla Sandeep over 9 years
      Hi, I have the same issue like you mentioned in question. Did you get the solution? Please provide me that.
    • 1218GG
      1218GG over 8 years
  • Andrew Smith
    Andrew Smith over 10 years
    This does cause the preferredStatusBarStyle method to be called, but still the status bar is black.
  • User 1531343
    User 1531343 over 10 years
    please see my updated answer..let me know quickly if that works or not
  • Andrew Smith
    Andrew Smith over 10 years
    My original question explicitly says that I need to do view by view control of the status bar.
  • User 1531343
    User 1531343 over 10 years
    can u please check your code with reference to my updated question?
  • Andrew Smith
    Andrew Smith over 10 years
    I've edited the original post, to show what I am actually doing. Still not working. Again, I need to set UIViewControllerBasedStatusBarAppearance to NO.
  • User 1531343
    User 1531343 over 10 years
  • Andrew Smith
    Andrew Smith over 10 years
    Yes, that link agrees with me, it does not work unless you set UIViewControllerBasedStatusBarAppearance to NO and set the toolbar globally.
  • mxcl
    mxcl over 10 years
    Do you mean [self setStatusBarNeedsUpdate] in the second block? (Or something else at least).
  • Nick
    Nick over 10 years
    this makes sense to me...great
  • mxcl
    mxcl over 10 years
    I believe it's because the UINavigationController’s preferredStatusBarStyle doesn’t call through to the ViewController it hosts, and instead just returns based on its navigationBarStyle.
  • Andrew Smith
    Andrew Smith over 10 years
    In this case the view is not inside a navigation controller.
  • joel.d
    joel.d over 10 years
    For me this was wrong. The key needed to be set to "Yes", as you would expect. I'm on Xcode 5.1 iOS 7.1, so maybe it's changed.
  • Arjun Mehta
    Arjun Mehta over 10 years
    I'm using Xcode 5.1 and iOS 7.1 as well and NO worked for me... STRANGE.
  • Hadi Sharghi
    Hadi Sharghi about 10 years
    Where should I add this key?
  • Saren Inden
    Saren Inden almost 10 years
    In your [AppName]-Info.plist file
  • bpolat
    bpolat almost 10 years
    It works fine when "View controller-based status bar" key set to "YES" with Xcode6.0, iOS 8.0
  • Matej
    Matej over 9 years
    Very counterintuitive to think that bar style takes preference over an implemented method in the view controller, and only when presenting modal views!
  • Thomás Pereira
    Thomás Pereira almost 9 years
    In Swift 2.0 You must remove "as? UIViewController" of the condition statement.
  • Unome
    Unome over 8 years
    Brilliant, I made one modification in addition to removing the "as" statement, I changed it from "first" to "last" this way whatever view controller is being seen by the user at the top of the stack will have the ability to control the color of the status bar. Awesome work, thanks for sharing!
  • bearMountain
    bearMountain over 8 years
    If your navigation controller doesn't have any view controllers, this would cause an infinite loop. return self.preferredStatusBarStyle() would call back into this exact same method.
  • Benjamin
    Benjamin over 8 years
    This is the only one that worked for me and I have to say I DO find it counter intuitive.
  • Mohd Asim
    Mohd Asim about 8 years
    Seems in iOS9 UIViewControllerBasedStatusBarAppearance by default contains value YES, as I needed to add it manually in .plist and set to NO to work properly.
  • Ric Santos
    Ric Santos about 8 years
    In my case, instead of using the rootViewController, I used the topViewController as during the stack the style may change.
  • Thanh-Nhon Nguyen
    Thanh-Nhon Nguyen almost 8 years
    UIBarStyleBlackTranslucent is deprecated, use UIBarStyleBlack instead
  • Kesava
    Kesava almost 8 years
    This is by far the best answer(For apps that use UINavigationController or UITabBarController
  • Admin
    Admin almost 8 years
    This post would be much improved if you could explain why and how this fixes the problem.
  • Cœur
    Cœur almost 8 years
    @Unome visibleViewController would be even better
  • Unome
    Unome almost 8 years
    Perfect! Thats even better @Cœur
  • Asad Ali
    Asad Ali over 7 years
    It will work only if you set "View controller-based status bar" key to "YES". I tested on Xcode8.1, iOS10.1
  • Marcel Zanoni
    Marcel Zanoni over 7 years
    For me it worked with YES too, but only after setting it to NO and then back to YES first.
  • Kyle Begeman
    Kyle Begeman over 7 years
    @bearMountain We are returning from super, not self.
  • Scott Jungwirth
    Scott Jungwirth about 7 years
    I ended up with the almost identical solution after struggling with this for hours.
  • AnBisw
    AnBisw over 6 years
    what is the usage for this?
  • Daniel Wood
    Daniel Wood over 6 years
    @Annjawn these methods are used by UIKit. You don't need to do anything other than add it to your project.
  • AnBisw
    AnBisw over 6 years
    @DanielWood yeah I figured that out and completely forgot that I used this exact same thing in one of my previous projects, although slightly differently.
  • wilforeal
    wilforeal over 6 years
    Instead of subclassing UINavigationController you can also just create an extension for UINavigationController and achieve the same result without having to subclass.
  • ricks
    ricks over 6 years
    2018 - navigationBar.barStyle = UIBarStyle.blackTranslucent
  • Andrew Plummer
    Andrew Plummer about 6 years
    At some point this seems to have been fixed, just using preferredStatusBarStyle in each VC works fine for me now.
  • Jaime S
    Jaime S about 6 years
    The UINavigationController is a container view controller, as mentioned by @mxcl it uses its default preferredStatusBarStyle. There is a childViewControllerForStatusBarStyle that is meant to be overridden by container view controller to provide the view controller for the preferred status bar style. In my opinion, the UINavigationController should provide by default the top most pushed view controller as its default implementation. Anyway, you may need to subclass (or use a category) for the nav controller to customize childViewControllerForStatusBarStyle .
  • nananta
    nananta almost 6 years
    Appears the recommended way now is to set navigation bar to... "isTranslucent = true" and "barStyle = .black".
  • Musa almatri
    Musa almatri almost 6 years
    This indeed the best answer
  • Supertecnoboff
    Supertecnoboff almost 6 years
    [self setNeedsStatusBarAppearanceUpdate]; such a great method, thank you!
  • P1xelfehler
    P1xelfehler over 5 years
    You can delete this key now as it is default in newer Xcode versions.
  • Mohamed Salah
    Mohamed Salah over 5 years
    Note that this way is deprecated from IOS 9.0
  • Arun Kumar
    Arun Kumar almost 5 years
    @DanielWood Bravo!! upvoted your answer and this is by far well deserved. The only problem I'm having now is when I dismiss modally presented screen (that screen had a lighter status bar and after dismissing it the new screen has default/darker status bar) it changes the status bar to light.
  • Andrew Kirna
    Andrew Kirna over 4 years
    This is a great answer pre iOS 13. However, this is now considered a "legacy customization" in iOS 13+. See my answer below 🙂
  • henrique
    henrique over 4 years
    Very complete answer, thanks! Also, something I struggled for a while, on iOS 13 the .default style takes dark mode into consideration and it's not documented, so if you're also supporting previous iOS versions you can add if #available(iOS 13, *) { return .darkContent } else { return .default } if you trying to setup the status bar style manually according to the color behind the status bar and that color is "bright".
  • Marc Etcheverry
    Marc Etcheverry over 4 years
    Note that the extension method of doing override var does not work anymore in Xcode 11.4/iOS 13.4
  • Marc Etcheverry
    Marc Etcheverry over 4 years
    Note that the extension method of doing override var does not work anymore in Xcode 11.4/iOS 13.4
  • Marc Etcheverry
    Marc Etcheverry over 4 years
    Note that the extension method of doing override var does not work anymore in Xcode 11.4/iOS 13.4
  • Vyacheslav
    Vyacheslav over 4 years
    @MarcEtcheverry so, why did you downvote the answer? it seems strange.
  • Marc Etcheverry
    Marc Etcheverry over 4 years
    Because extending objective C classes in Swift is implemented through Objective C categories. Overriding methods through Objective C categories is not recommended and likely to break. See stackoverflow.com/a/38274660/2438634
  • Marc Etcheverry
    Marc Etcheverry over 4 years
    This is wrong and it breaks in iOS 13.4. Because extending objective C classes in Swift is implemented through Objective C categories. Overriding methods through Objective C categories is not recommended and likely to break. See stackoverflow.com/a/38274660/2438634
  • Marc Etcheverry
    Marc Etcheverry over 4 years
    Because extending objective C classes in Swift is implemented through Objective C categories. Overriding methods through Objective C categories is not recommended and likely to break. See stackoverflow.com/a/38274660/2438634
  • Marc Etcheverry
    Marc Etcheverry over 4 years
    Because extending objective C classes in Swift is implemented through Objective C categories. Overriding methods through Objective C categories is not recommended and likely to break. See stackoverflow.com/a/38274660/2438634
  • Vyacheslav
    Vyacheslav over 4 years
    @MarcEtcheverry 'not recommended' != 'never use it!'. for jul2018 the answer was correct. Even this answer is not up-to-date this is not a reason to downvote it. I can't see the future
  • Igor Vasilev
    Igor Vasilev almost 4 years
    Although overriding the UINavigationController definitely works, it looks like a bug on Apple side that it doesn't do the childForStatusBarStyle by default returning its topViewController. Eg UITabBarController does this for its tabs. To me, there's no reason why UINavigationController, being a strictly container controller for hosting "real" View controllers rather than presenting its own UI, should "eat up" those status bar stylings. Will create a radar for Apple.