preferredStatusBarStyle not respecting on iOS 13 - ios

I'm working in my simulator on a single view app with a dark background. It's a UIViewController wrapped in a UINavigationController.
In my view controller I have override var preferredStatusBarStyle: UIStatusBarStyle { .lightContent }
In my info.plist I have View controller-based status bar appearance = YES
And yet when I run it it shows white for a second and then jumps to having black text.
What's going on here? Is there a fix?
Edit: I've tried .default, .lightContent and .darkContent just to be sure, nothing works

I recently ran into this problem and these extensions seemed to fix the issue.
extension UITabBarController {
open override var childForStatusBarStyle: UIViewController? {
return selectedViewController?.childForStatusBarStyle ?? selectedViewController
}
}
extension UINavigationController {
open override var childForStatusBarStyle: UIViewController? {
return topViewController?.childForStatusBarStyle ?? topViewController
}
}
I just put them into a file called UIViewController+StatusBar.swift and included it in the project.

The correct answer referenced by #matt is navigationController?.navigationBar.barStyle = .lightContent in viewDidLoad.

Related

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 :)
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() }
}
}
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.)
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
}

UINavigation Status bar color change in swift iOS [duplicate]

This question already has answers here:
preferredStatusBarStyle var not working in iOS12?
(7 answers)
Closed 3 years ago.
One of my Controller is having navigation status bar color is black, I want to make it white. how can I change it?
As some people already suggested, inside your viewController set:
override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent }
But, this is not enough. Since that viewController is contained inside UINavigationController, you need to say to nav controller to use status bar style based on currently displayed controller. One way to do this is by extending UINavigationController like this:
extension UINavigationController {
open override var preferredStatusBarStyle: UIStatusBarStyle {
return topViewController?.preferredStatusBarStyle ?? .default
}
}
The preferredStatusBarStyle property is set to lightContent. Build and Run the project to see the content of the status bar changed to light.
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
The content of the status bar is dark again, which is the default. The reason for this is, iOS asked for the style of the status bar of the navigation controller instead of the contained view controller.
To change the style of the navigation controller to lightinside the app, add the following viewDidAppear(_:) method
override func viewDidAppear(_ animated: Bool) {
navigationController?.navigationBar.barStyle = .black
}
Add viewWillAppear to your code
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let statusBar: UIView = UIApplication.shared.value(forKey: "statusBar") as! UIView
if statusBar.responds(to:#selector(setter: UIView.backgroundColor)) {
statusBar.backgroundColor = UIColor.white
}
let img = UIImage()
navigationController?.navigationBar.shadowImage = img
navigationController?.navigationBar.setBackgroundImage(img, for: UIBarMetrics.default)
navigationController?.navigationBar.backgroundColor = UIColor.white
navigationController?.navigationBar.barTintColor = UIColor.white
}
By default we can set Status bar Style in our project plist but if you want to define color for specific controller then we should override following method in your controller class .
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
Hope it will help you :)
Call this inside your ViewController:
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
Updated for Swift 5:
override var preferredStatusBarStyle: UIStatusBarStyle {
.lightContent
}
Also, if you don't already have made it, you should set the View controller-based status bar appearance property from your info.plist file to YES.

Setting statusbarStyle (deprecated in iOS 9.0)

Just downloaded the new xCode 10.0 and saw that the old statusBarStyle has been deprecated since iOS 9.0.
Warning: Setter for 'statusBarStyle' was deprecated in iOS 9.0: Use -[UIViewController preferredStatusBarStyle]
Deprecated code:
UIApplication.shared.statusBarStyle = .default
I tried using self.preferredStatusBarStyle, but found out the property is only a getter. So anyone knows how to set the statusBarStyle?
Edit
I want to change the statusBarStyle inside a function, where a user can switch between different themes. For example:
func changeStatusBar(toDarkMode: Bool) {
if toDarkMode {
// Set to light statusBarStyle
} else {
// Set to default
}
}
Add View controller-based status bar appearance NO in Info.plist
And select Light in Status Bar Style in Deployment Info
Set your darkMode variable using the same code you have now, then use it in the computed variable that the system is expecting:
var darkMode = false
override var preferredStatusBarStyle : UIStatusBarStyle {
return darkMode ? .default : .lightContent
}
Depending on the context you may need to force a refresh of the screen for it to take effect. You would do that with the following call:
setNeedsStatusBarAppearanceUpdate()
In swift4, You can use this block of code below viewDidLoad() in your ViewController-
override var preferredStatusBarStyle : UIStatusBarStyle {
return .lightContent
}
If you use UINavigationController you also may want to use following code :
extension UINavigationController {
open override var preferredStatusBarStyle: UIStatusBarStyle {
return topViewController?.preferredStatusBarStyle ?? .default
}
}
Reason is setNeedsStatusBarAppearanceUpdate() doesn't call childs preferredStatusBarStyle
None of the other suggestions worked for me. I ended up getting it to work by:
Setting:
override var preferredStatusBarStyle : UIStatusBarStyle {
return .lightContent
}
Calling:
setNeedsStatusBarAppearanceUpdate()
My solution was as this:
making an extension from the navigation controller:
extension UINavigationController {
open override var preferredStatusBarStyle: UIStatusBarStyle {
if let topViewController = presentedViewController{
return topViewController.preferredStatusBarStyle
}
if let topViewController = viewControllers.last {
return topViewController.preferredStatusBarStyle
}
return .default
}
}
and if you have a viewController that will have another style than the style of the app , you can make this
var barStyle = UIStatusBarStyle.lightContent
override var preferredStatusBarStyle: UIStatusBarStyle{
return barStyle
}
lets say that you app status style is .default and you want this screen to be .lightContent
so barStyle will take the .lightContent as its default value, this will change the status bar style to lightContent, and then make sure when viewWillDisappear change the barStyle again to the app status bar style which in our case is .default .
this is works for me

Preferred status bar style of view controller is ignored when in navigation controller

I'm writing an iOS App with multiple views. I've set the App to use ViewController-based status bar style, which allows me to use the following code
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
That worked like expected.
But then I've embedded the views in a navigation controller and connected a BarButtonItem with a showSegue. Since then the ViewController of the view switched to ignores the style settings and shows the default black status bar.
When you're in a navigation controller that will not get called. The navigation controller's preferredStatusBarStyle will be called. Try this along with your code:
extension UINavigationController {
open override var preferredStatusBarStyle: UIStatusBarStyle {
return topViewController?.preferredStatusBarStyle ?? .default
}
}
There is a solution that is a bit more concise (and recommended by Apple):
extension UINavigationController {
override open var childForStatusBarStyle: UIViewController? {
return topViewController
}
}

setStatusBarStyle:animated: deprecated

I'm currently developing an app (in Swift 3) on Xcode 8 beta, for iOS 10.
What I want to achieve is to change status bar style within a view controller at run time, for changing the theme from daytime theme to night theme.
I've found out that the method I used to use when I was developing another app in the past was deprecated, as shown here on the API reference.
However, preferredStatusBarStyle won't work here since I would like to change it within a single view controller.
Can anybody think of other ways to perform this?
Thanks in advance
EDIT:
To be clear, what I want to do is to change the style when the view controller is already on screen.
You can create a statusBarStyle variable that when changed updates the status bar appearance. If you only want this to affect one controller, simply reverse the effect when the Controller will or did disappear.
var statusBarStyle: UIStatusBarStyle = .lightContent {
didSet {
setNeedsStatusBarAppearanceUpdate()
}
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return statusBarStyle
}
The above solution will override the previous controller's status bar style before the controller appears. If you want to change the status bar style when the controller appears, try this:
var statusBarStyle: UIStatusBarStyle? {
didSet {
setNeedsStatusBarAppearanceUpdate()
}
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return statusBarStyle ?? super.preferredStatusBarStyle
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
statusBarStyle = .lightContent
}
In your info.plist, add the UIViewControllerBasedStatusBarAppearance key with a value of false.
Then, in your viewController when switching to your night theme:
UIApplication.shared.statusBarStyle = .lightContent
To go back to black:
UIApplication.shared.statusBarStyle = .default

Resources