How do I prevent iOS 13's Dark Mode from changing the text color in my app's status bar?
Solution 1
iOS 13 Solution(s)
UINavigationController
is a subclass of UIViewController
! (who knew 🙃)
Therefore, when presenting view controllers embedded in navigation controllers, you're not really presenting the embedded view controllers; you're presenting the navigation controllers! UINavigationController
, as a subclass of UIViewController
, inherits preferredStatusBarStyle
and childForStatusBarStyle
, which you can set as desired.
Any of the following methods should work:
-
Opt out of Dark Mode entirely
- In your
info.plist
, add the following property:- Key -
UIUserInterfaceStyle
(aka. "User Interface Style") - Value - Light
- Key -
- In your
-
Override
preferredStatusBarStyle
withinUINavigationController
-
preferredStatusBarStyle
(doc) - The preferred status bar style for the view controller -
Subclass
or extendUINavigationController
class MyNavigationController: UINavigationController { override var preferredStatusBarStyle: UIStatusBarStyle { .lightContent } }
ORextension UINavigationController { open override var preferredStatusBarStyle: UIStatusBarStyle { .lightContent } }
-
-
Override
childForStatusBarStyle
withinUINavigationController
-
childForStatusBarStyle
(doc) - Called when the system needs the view controller to use for determining status bar style - According to Apple's documentation,
"If your container view controller derives its status bar style from one of its child view controllers, [override this property] and return that child view controller. If you return nil or do not override this method, the status bar style for self is used. If the return value from this method changes, call the setNeedsStatusBarAppearanceUpdate() method."
-
In other words, if you don't implement solution 3 here, the system will fall back to solution 2 above.
-
Subclass
or extendUINavigationController
class MyNavigationController: UINavigationController { override var childForStatusBarStyle: UIViewController? { topViewController } }
ORextension UINavigationController { open override var childForStatusBarStyle: UIViewController? { topViewController } }
-
You can return any view controller you'd like above. I recommend one of the following:
-
topViewController
(ofUINavigationController
) (doc) - The view controller at the top of the navigation stack -
visibleViewController
(ofUINavigationController
) (doc) - The view controller associated with the currently visible view in the navigation interface (hint: this can include "a view controller that was presented modally on top of the navigation controller itself")
-
-
Note: If you decide to subclass UINavigationController
, remember to apply that class to your nav controllers through the identity inspector in IB.
Edits: Strikethrough edits were made to remove extensions as a suggested answer. Other developers noted that they stopped working in Xcode 11.4 and Apple's documentation discourages the use of this ambiguous behavior.
P.S. My code uses Swift 5.1 syntax 😎
Solution 2
If you set the UIViewControllerBasedStatusBarAppearance
key in your app's info.plist
to YES
, you can override the status bar style in your currently presented view controller:
override var preferredStatusBarStyle: UIStatusBarStyle {
if #available(iOS 13, *) {
return .darkContent
} else {
return .default
}
}
and call the setNeedsStatusBarAppearanceUpdate() method
Solution 3
You can write extension to UIStatusBarStyle
:
extension UIStatusBarStyle {
static var black: UIStatusBarStyle {
if #available(iOS 13.0, *) {
return .darkContent
}
return .default
}
}
And then you can easily use in your ViewControllers:
override var preferredStatusBarStyle: UIStatusBarStyle {
.black
}
Solution 4
You can try to make your navigation bar always light
if #available(iOS 13.0, *) {
navigationController?.navigationBar.overrideUserInterfaceStyle = .light
}
Solution 5
I have done something like this.
I have toggler function wich toggles Status bar style depending on View Controller displayed
func toggleLight() {
self.navigationBar.barTintColor = AppColors.White
isDarkStyle = false
setNeedsStatusBarAppearanceUpdate()
}
and here is most important part
override var preferredStatusBarStyle: UIStatusBarStyle {
if #available(iOS 13.0, *) {
return isDarkStyle ? .lightContent : .darkContent
}
return isDarkStyle ? .lightContent : .default
}
Where isDarkStyle represents navigation bar background colour dark or light. If it is dark then text (content) should be light, if it is light than text (content) should be default or from iOS 13 dark.
To sum up: .lightContent, .darkContent
displays independent of Dark Mode as it is supposed to do. While .default
is susceptible to Dark Mode changes!
Related videos on Youtube
EvGeniy Ilyin
Updated on August 05, 2021Comments
-
EvGeniy Ilyin almost 3 years
My navigation bar has a
white
backgroundColor
and my status bar uses thedark
textColor
. When a user changes the iOS theme to Dark Mode, the status bar changes towhite
text on awhite
background. As a result, I can't see anything. How can I disable this change for my app?-
rmaddy almost 5 yearsIt sounds like you don't wish to support darkMode at all. You can opt out of supporting darkMode. That's been covered here.
-
EvGeniy Ilyin almost 5 yearsyes You right, but > here < - where? I do not see link
-
rmaddy almost 5 yearsI meant Stack Overflow. Search on opting out of dark mode.
-
DoesData almost 5 years
-
shim over 4 yearsEven if you implement the suggestions in those posts you will likely still have problems with the status bar unless you override its default behaviour already in your app.
-
-
Andrew Kirna over 4 yearsI am using both of those configurations but Dark Mode still overrides the status bar color.
-
Frank Rupprecht over 4 years@AndrewKirna Hard so say what the issue might be. Do you maybe want to post a question for that?
-
Andrew Kirna over 4 yearsOk, I’ll consider posting. It seems Apple wants me to use a dynamic color for my nav bars, however, I don’t want to do that because it uses my company’s branding. I’m guessing your solution has the same problem I’m experiencing (black status bar text in light mode; white status bar text in dark mode) despite overriding
preferredStatusBarStyle
in each view controller. Have you built it on Xcode 11 for iOS 13? -
Frank Rupprecht over 4 yearsYes, works perfectly for me. Are you sure you override it in the main presented view controller?
-
Andrew Kirna over 4 yearsThe presented view controller is a subclass of a view controller that overrides the property. I use a base class. Do you think that’s an issue?
-
Frank Rupprecht over 4 yearsI don't think so... Are you really sure you set the
UIViewControllerBasedStatusBarAppearance
in theinfo.plist
toYES
? -
Andrew Kirna over 4 yearsYup. I also set “Status bar style” to “Light Content” in
info.plist
. Do you think that would affect it? -
Frank Rupprecht over 4 yearsIt should not, but wouldn't hurt to test removing that key. Are you using a navigation controller? If so, this might help: stackoverflow.com/a/19365160/541016
-
Andrew Kirna over 4 yearsThat answer is what I've been using for iOS < 13. It uses the old API of setting the
barStyle
property to control both the tint of bar buttons in the nav bar and the tint of the status bar (i.e. settingbarStyle = .black
accomplished my desired white text over a dark background). iOS 13 requires a new implementation... -
Andrew Kirna over 4 yearsFrank, I followed a bunch of links related to your link above and eventually discovered what I needed! Thanks for the help. I posted my solution below!
-
Marc Etcheverry over 4 yearsNote that the extension method of doing override var does not work anymore in Xcode 11.4/iOS 13.4
-
alextudge about 4 years@MarcEtcheverry do you know what the alternative is for 13.4?
-
Marc Etcheverry about 4 yearsSubclass any of those container view controllers and override there, which is the proper way to do this. Swift extensions for ObjC classes may be implemented using ObjC categories, which never could safely override things. You could swizzle it in ObjC, but subclassing is safer.
-
Mohamed Aziz Bessrour about 4 yearsI'm using Xcode 11.4 and right now, I have view controller presented on a custom UINavigationController and others that are presented on a native UINavigationController. childForStatusBarStyle is only called for the view controllers that are presented on the native UINavigationController. This is so annoying. Does anyone have any idea why ?
-
Mohamed Aziz Bessrour about 4 yearsI'm using Xcode 11.4 and right now, I have view controller presented on a custom UINavigationController and others that are presented on a native UINavigationController. childForStatusBarStyle is only called for the view controllers that are presented on the native UINavigationController. This is so annoying. Does anyone have any idea why ?
-
Andrew Kirna about 4 years@MarcEtcheverry, thanks for pointing that out. Do you have any documentation for this so I can update my answer?
-
Marc Etcheverry about 4 years"If the name of a method declared in a category is the same as a method in the original class, or a method in another category on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime. This is less likely to be an issue if you’re using categories with your own classes, but can cause problems when using categories to add methods to standard Cocoa or Cocoa Touch classes". developer.apple.com/library/archive/documentation/Cocoa/…
-
Marc Etcheverry about 4 yearsYou can follow all of links and answers here: stackoverflow.com/a/38274660/2438634 I think the issue lies in that people forget that extensions on Objective C objects will end up being implemented as ObjC categories, which have this problem. So, forget about overriding, and all extensions members should be prefixed as well unless they are @nonobjc.
-
Lance Samaria about 4 yearssubclassing UINavigationController is the only thing that worked for me because my info.plist has View controller-based status bar appearance = YES
-
Ammar Mujeeb almost 4 yearsI worked for Darkmode (have to make it same as light mode as per Client requirement) then after finished my work just saw this that we can exclude support for darkmode just with a keyword! :D good for next time atleast..!