How do I prevent iOS 13's Dark Mode from changing the text color in my app's status bar?
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 controllerSubclass
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 stackvisibleViewController
(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
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
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
}