2

I'm working on a swift app and I wanna change the UIStatusBarStyle according to the app's theme (there are 2 options - light and dark theme). I have set View controller-based status bar appearance to NO in the info.plist and in the UIViewController I tried to set it based on the current theme like so:

override var preferredStatusBarStyle: UIStatusBarStyle {
    return Theme.current.statusBarStyle
}


protocol ThemeProtocol { 
    // Status Bar
    var statusBarStyle: UIStatusBarStyle { get } 
}

class Theme {
    static var current: ThemeProtocol = LightTheme()
}

class LightTheme: ThemeProtocol {
    // Status Bar 
    var statusBarStyle: UIStatusBarStyle = .default

}

class DarkTheme: ThemeProtocol {
    // Status Bar 
    var statusBarStyle: UIStatusBarStyle = .lightContent

}

No result really. I tried to test it by returning only: return .lightContent but that didn't change the status bar either.

What am I doing wrong?

UPDATE:

Okay, so this is what I'm trying to do and it's not working.

    fileprivate func applyTheme() {

    statusBarStyle = UserDefaults.standard.bool(forKey: SelectedThemeKey) ? .default : .lightContent
    self.setNeedsStatusBarAppearanceUpdate()

}

override var preferredStatusBarStyle: UIStatusBarStyle {
    return statusBarStyle
}

And it's not working. Despite changing the theme, the status bar always remain with the default style. applyTheme() is called in viewDidLoad() and viewWillAppear()

Dani
  • 3,427
  • 3
  • 28
  • 54

3 Answers3

6

You need to call this method after any change

setNeedsStatusBarAppearanceUpdate()

//

class ViewController: UIViewController {

    var current = UIStatusBarStyle.default

    @IBAction func changeClicked(_ sender: Any) {
        current = current == .default ? .lightContent : .default
        self.setNeedsStatusBarAppearanceUpdate()
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }
    override  var preferredStatusBarStyle: UIStatusBarStyle {
        return current
    }

}

enter image description here

Shehata Gamal
  • 98,760
  • 8
  • 65
  • 87
  • I'm trying replicate what you've shared and for some reason it's not working. Can you please take a look at the updated code on my post above? – Dani Aug 11 '18 at 12:42
  • this is created on default project settings so roll back to every change you made inside info.plist – Shehata Gamal Aug 11 '18 at 12:46
  • Yes, i just deleted `View controller-based status bar appearance` from the `info.plist` and it's all working now. Thank you! – Dani Aug 11 '18 at 12:49
  • 1
    you set it to No when you want to hide it completely – Shehata Gamal Aug 11 '18 at 12:50
  • What does it mean when you write current = current == .default ? .lightContent : .default ? .... I understand that current == .default means the value of current is equal to .default but I'm trying to understand the rest. – bobcat Sep 12 '18 at 23:29
  • @bhealth it's equivalent to **if---else** look for Ternary operator in swift – Shehata Gamal Sep 12 '18 at 23:30
4

First of all, your code makes no sense. You say

I have set View controller-based status bar appearance to NO in the info.plist and in the UIViewController I tried to set it

So on the one hand you tell the runtime, do not listen to my view controller. Then you complain when the runtime doesn't listen to your view controller!

Second, keep in mind that your view controller gets no say in the status bar appearance unless it is the top-level view controller (or the top-level view controller deliberately defers to it). If your view controller is inside a navigation controller, for example, its statusBarStyle is completely irrelevant.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • I actually read that you need to set `View controller-based status bar appearance` to `NO` for that to work and it made no sense to me, but that's very wrong. Thank you for pointing it out. And for the additional information – Dani Aug 11 '18 at 12:48
4

First

if you are trying to change StatusBarStyle with UIApplication.shared.statusBarStyle = .default, then you should set View controller-based status bar appearance to be NO.

in this way, you need to control UIApplication.shared.statusBarStyle by yourself in everywhere; e.g viewWillAppear / viewWillDisappear.

but this method is deprecated after iOS 9.

Now I suggest you learn the new way:

please set View controller-based status bar appearance to be YES, and

override UIViewController's preferredStatusBarStyle to handle it.

override var preferredStatusBarStyle: UIStatusBarStyle {
    return .default
}

but it still not working, why?

in your case, I guess that UIViewController is one of viewControllers of UINavigationController.

if you used UINavigationController, you should override preferredStatusBarStyle in UINavigationController.

maybe you will say that I need to set different status bar style with difference view controllers, it is okay.

Just override childForStatusBarStyle in UINavigationController like this

override var childForStatusBarStyle: UIViewController? {
    return topViewController
}

then override preferredStatusBarStyle in UIViewController you want to change.

you can also make it by extension like this

extension UINavigationController {
    override open var childForStatusBarStyle: UIViewController? {
        return topViewController
    }
}
張家齊
  • 489
  • 4
  • 4