'iOS 13 Status bar style invalid (childForStatusBarStyle never called)

I've been searching a lot, but didn't find the same problem as my on StackOverflow or anywhere else.


Setup

  • Info.plist
    • ViewControllerBasedStatusBar set to YES
    • StatusBarStyle set to .lightContent
    • UserInterfaceStyle set to .light (app doesn't support .dark mode)
  • Each UIViewController has its own implementation of preferredStatusBarStyle:
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }
    
  • UITabBarController has extension:
    open override var childForStatusBarStyle: UIViewController? {
        return selectedViewController
    }
    
  • UINavigationController has extension:
    override open var childForStatusBarStyle: UIViewController? {
        return topViewController
    }
    

Problem

Since iOS 13 released my status bar logic was broken and I can't understand why. On iOS <= 12.4 everything works properly. childForStatusBarStyle is never called and each viewController has some random style.

UPDATE: Since iOS 13 released status has style based on UserInterfaceStyle set global, not based on preferredStatusBarStyle(with proper setup) in case of UITabBarController -> UINavigationController -> UIViewController hierarchy.


Question

The question is how to solve this problem? Did something silently changed in this logic? (Checked many articles and changelogs)


Reproduction

I've been able to reproduce the bug in the sample project with everything set up as mentioned above.

Here I have github project which contains view hierarchy as follows:

CustomTabBarController
 - UINavigationController
   - CustomViewController
 - CustomViewController

Now, when you select the first tab app has dark style status bar, when the second selected light style one. CustomViewController has preferredStatusBarStyle set to .lightContent.

More:

  • Xcode: Version 11.5 (11E608c)
  • Device: iPhone 8 Simulator
  • iOS: Version 13.5

P.S: I'm ready and will provide more details on the topic, don't hesitate to ask me to do so. Project is running more than 2 years and thing like this is really to debug :)



Solution 1:[1]

In a navigation controller situation, the status bar style (light/dark) does not depend, and has never depended, on anything except the navigation bar style. Add this line in your project's custom tab bar:

    let bugVC = UINavigationController(rootViewController: ViewController())
    bugVC.navigationBar.barStyle = .black // *

Now the status bar text is white in both of the tab bar controller's children. (And if you then don't like the color of the navigation bar, which is the default black, you can change it; that won't affect the behavior of the status bar.)

Solution 2:[2]

For those to whom using .barStyle is a big deal in case of time, there is a workaround. Subclass UINavigationController, then call setNeedsStatusBarAppearanceUpdate each time viewControllers change.


Sample code

class WorkaroundNavigationController: UINavigationController {
    override var childForStatusBarStyle: UIViewController? {
        return topViewController
    }
    
    override var viewControllers: [UIViewController] {
        didSet { setNeedsStatusBarAppearanceUpdate() }
    }
}

Solution 3:[3]

For UINavigationController - UIViewController structure, add code below to navigation controller. Then override child view controller's preferredStatusBarStyle, it worked for me.

override var childForStatusBarStyle: UIViewController? {
    visibleViewController
}

override var preferredStatusBarStyle: UIStatusBarStyle {
    visibleViewController?.preferredStatusBarStyle ?? .default
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 matt
Solution 2 ????
Solution 3