Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS: Default status bar style with UIViewControllerBasedStatusBarAppearance YES

Is there a way how to set the default status bar style while keeping the UIViewControllerBasedStatusBarAppearance enabled?

Here is the problem, I'm dealing with:

Nearly the whole app needs to be using UIStatusBarStyle.LightContent as the navigation bar has a dark background. Originally, UIViewControllerBasedStatusBarAppearance was disabled and the following was set in in Info.plist for while text status status bar:

<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>

This worked just fine until I found out that this .LightContent status bar is shown even for some of the share extensions, like Facebook Messenger, causing it to be unreadable:

Facebook Messenger Share Extension light status bar style

This could be solved by using UIViewControllerBasedStatusBarAppearance, but then I would need to add the following method to all of view controllers which I want to avoid as the app is quite large.

Moreover, for the one screen in the app that has light nav bar background, I was switching to dark nav bar using UIApplication.sharedApplication().setStatusBarStyle() but this method in deprecated in iOS 9.

Any ideas how to solve this? Swizzling?

like image 405
Tom Kraina Avatar asked Jan 27 '17 14:01

Tom Kraina


2 Answers

Solution

The easiest and cleanest way how to achieve that is to add the following line in AppDelegate's application:willFinishLaunchingWithOptions method:

UINavigationBar.appearance().barStyle = .Black

This will make .LightContent as the default status bar style thorough the app as long as your app uses UINavigationController.

Don't forget to keep the following setting in app's Info.plist if want to use .LightContent status bar style during launch for splash screen:

<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>

TL;DR

My current setup, which is very similar to many other apps, uses UITabBarController as the top most controller with UINavigationController stack for each tab.

UINavigationController takes care of the status bar style (as it should) and do not call preferredStatusBarStyle() on its child view controllers. Therefore, implementing the subclassing solution proposed by par does not work in my case.

Further subclassing the custom subclass of UINavigationController I'm using would not be a clean solution either.

Now, since the app has UIViewControllerBasedStatusBarAppearance enabled and correct status bar style everywhere in the app itself, SFSafariViewController and share extension like Messages, Mail, etc use the correct (.Default) status bar style too.

The only exception where the correct status bar style is not used is the Facebook Messenger's share extension mentioned in the question. However, it seems to be a bug in the extension itself as all apps I have tried that use .LightContent status bar style (like Twitter, for example) have the same issue - presented FB Messenger share extension from the app has a status bar with white color text.

like image 168
Tom Kraina Avatar answered Nov 20 '22 17:11

Tom Kraina


A solution I use quite frequently is to create a base view controller class that all view controllers in my app derive from. This has the advantage of allowing use of the view-controller-based status bar style-setting functionality with a default (light or dark) style, which can then be overridden on a per-view-controller basis as necessary.

A base view controller is also really handy once you start getting into trait-collection based changes, custom transition animations that you want for most view controllers, a central point for analytics tracking, and other useful things.

Yes, you have to go through your potentially large source base and change all your UIViewControllers into BaseViewControllers, but this is often as easy as a global search-and-replace.

Here's what the BaseViewController looks like with status-bar related methods:

class BaseViewController: UIViewController {
    var statusBarHidden: Bool = false { didSet { setNeedsStatusBarAppearanceUpdate() } }
    var statusBarStyle: UIStatusBarStyle = .lightContent { didSet { setNeedsStatusBarAppearanceUpdate() } }
    var statusBarUpdateAnimation: UIStatusBarAnimation = .fade { didSet { setNeedsStatusBarAppearanceUpdate() } }

    override var preferredStatusBarStyle: UIStatusBarStyle { return statusBarStyle }
    override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { return statusBarUpdateAnimation }
    override var prefersStatusBarHidden: Bool { return statusBarHidden }
}

For all view controllers that use the default light style, you don't need to do anything special:

class ViewController: BaseViewController { }

In the cases where you need a dark status bar, do:

class DarkStatusBarViewController: BaseViewController {
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        statusBarStyle = .default
    }
}

Note also that you could rename the DarkStatusBarViewController above to DarkStatusBarBaseViewController and derive from it instead of BaseViewController when you need a dark status bar. Then you don't need to duplicate the status bar code in every view controller that needs it and you maintain a nice linear relationship for all your BaseViewController functionality.

like image 38
par Avatar answered Nov 20 '22 16:11

par