3

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 :)

えるまる
  • 2,409
  • 3
  • 24
  • 44

3 Answers3

4

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() }
    }
}
えるまる
  • 2,409
  • 3
  • 24
  • 44
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.)

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • If you look at extensions I've implemented for both(`UITabBarController` and `UINavigationController`) method `childForStatusBarStyle` and in the first tab there have to be `preferredStatusBarStyle` of `ViewController`. – えるまる Aug 16 '20 at 14:04
  • No need to be hostile. If you look at my answer it tells you how to make the status bar be light in a navigation controller situation. I stand by that. What I'm telling you has always been the right way. See for instance my answer here: https://stackoverflow.com/questions/47837959/updating-the-status-bar-style-between-view-controllers/47838772#47838772 – matt Aug 16 '20 at 14:10
  • Thanks for your answer! There is nothing about hostile, really appreciate your help! I gonna check it out. – えるまる Aug 16 '20 at 14:16
  • I've just downgraded the sample project to `iOS 12.4` deployment target and it's working as I expected (Always been working). Please, try to run it on `12.4` simulator. Print in `childForStatusBarStyle` is called and `UINavigationController`'s status bar style set to be what returned in `preferredStatusBarStyle` of `ViewController` class. – えるまる Aug 16 '20 at 14:38
  • Then, try to run again on `13+` iOS and print won't be called. So this is the question I've asked: "The question is how to solve this problem? Did something silently changed in this logic? (Checked many articles and changelogs)" – えるまる Aug 16 '20 at 14:40
  • Of course yes, it changed, But what you were doing was always wrong. I'm trying to tell what was always the _right_ way. – matt Aug 16 '20 at 14:44
  • Thanks for clarifying such a tricky moment, does there exist any source that states about deprecating the wrong use of `childForStatusBarStyle` in `UINavigationController`? – えるまる Aug 17 '20 at 06:14
0

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
}
Roger Lee
  • 200
  • 1
  • 7