SideMenu in Swift ignores UIStatusBarStyle.lightContent - ios

I am using a third party library called SideMenu, which can be found here. It works like a charm, but in my app, I use a white status bar UIStatusBarStyle.lightContent. I set this up in the entire project: in the deployment info and programmatically:
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
In viewDidLoad(), I also call setNeedsStatusBarAppearanceUpdate(). It works on all ViewControllers, except when I use the SideMenu. When it appears, the text of the StatusBar becomes black and when I close it, it doesn't turn white again. It only becomes white when I go to another ViewController where the preferredStatusBarStyle is overridden.
Any ideas on how I can fix this? Do I need to call setNeedsStatusBarAppearanceUpdate() somewhere else? I have tried to look at the source code of SideMenu and tried to implement it in the transition function, but this was unsuccessful as well.
Note: I have set up my project with .lightContent in the deployment info, but still my ViewControllers show black status bar if I don't override the preferredStatusBarStyle. Maybe this can be the issue as well - it is by default not white but black, even though I set it to .lightContent?
EDIT: I also use these extensions to make the color of the StatusBar work using TabBarController and NavigationController. Issue could be in here as well:
extension UITabBarController {
open override var childForStatusBarStyle: UIViewController? {
return selectedViewController
}
}
extension UINavigationController {
open override var childForStatusBarStyle: UIViewController? {
return visibleViewController
}
}

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
}

preferredStatusBarStyle not respecting on iOS 13

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.

Unable to change status bar style

I have a UIViewController VC, which embedded in UINavigationController NAV. The NavigationController is embedded in UITabBarController TAB. Now, I need every view controller in my app but VC to have .default status bar style. VC has to have lightContent status bar style.
What I’ve done to achieve the desired effect:
Set View controller-based status bar appearance in Info.plist to YES.
Overriden preferredStatusBarStyle property for every view controller that can be displayed by NAV including VC.
Put setNeedsStatusBarUpdate() into viewDidLoad() of every view controller that can be displayed by NAV including VC.
Such an approach resulted in nothing.
Since TAB is initial view controller inside my Main.storyboard and everything is essentially displayed through it, I thought that maybe I can change status bar through it. So I’ve written the following inside TAB description:
// MARK: Status bar
///
private var requiredStatusBarStyle: UIStatusBarStyle = .default
//
override var preferredStatusBarStyle: UIStatusBarStyle { get { return self.requiredStatusBarStyle } }
///
func setStatusBarStyle(_ style: UIStatusBarStyle)
{
requiredStatusBarStyle = style
DispatchQueue.main.async
{
self.setNeedsStatusBarAppearanceUpdate()
}
}
And then invoked setStatusBarStyle method of TAB by every view controller that can be displayed by NAV including VC in viewWillAppear method. This resulted in nothing as well. Funny thing is invoking setStatusBarStyle inside TAB’s viewDidLoad does nothing too.
Well, if it's not TAB that is preventing me from changing style, maybe NAV is? So I've made the following extension:
struct UINavigationControllerExtensionKeys
{
static var UIStatusBarStyleKey: UInt8 = 0
}
extension UINavigationController
{
private var requiredStatusBarStyle: UIStatusBarStyle
{
get
{
guard let style = objc_getAssociatedObject(self, &UINavigationControllerExtensionKeys.UIStatusBarStyleKey) as? UIStatusBarStyle else
{
return .default
}
return style
}
set (style)
{
objc_setAssociatedObject(self, &UINavigationControllerExtensionKeys.UIStatusBarStyleKey, style, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
open override var preferredStatusBarStyle: UIStatusBarStyle { get { return self.requiredStatusBarStyle } }
func setStatusBarStyle(_ style: UIStatusBarStyle)
{
requiredStatusBarStyle = style
DispatchQueue.main.async
{
self.setNeedsStatusBarAppearanceUpdate()
}
}
}
And then invoked setStatusBarStyle method of NAV by every child view controller of it in viewWillAppear method. No luck as well.
It is obvious that I'm overcomplicationg things here. But for some reason answers poroposed here and here do not work for me. I am lost here people. Please help.
Now, I need every view controller in my app but VC to have .default
status bar style. VC has to have lightContent status bar style.
For me, you're merely giving yourself a headache with your approaches. Here's my take and it will be lot easier.
Make a LightBaseViewController: UIViewController or whatever base class name you want for all the viewControllers you want to have the light status bar, and put this in LightBaseViewController's viewWillAppear:
UIApplication.shared.statusBarStyle = .lightContent
Then of course make a DarkBaseViewController and make the status bar dark by making statusBarStyle property back to default.
UIApplication.shared.statusBarStyle = .default
Subclass either the dark or light base view controllers.
Finally, set this property to NO in your info.plist:
View controller-based status bar appearance
Hope this helps.

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