Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to switch programmatically to dark mode swift

How can I make a switch to change programatically to dark or light mode in my iOS app? I'm using Swift.

like image 342
TheCesco1988 Avatar asked Feb 11 '20 14:02

TheCesco1988


People also ask

How do I change dark programmatically in Swift?

You can opt dark mode as per UIViewcontroller or UIView by setting overrideUserInterfaceStyle property to dark otherwise in Xcode 11 the by default it is dark.

How do I enable Dark mode in Xcode?

On a device, you can enable Dark Mode by navigating to the Display & Brightness page in the Settings app. However, it’s a lot easier during development to add an option to the Control Centre to quickly switch between dark and light mode: While working in Xcode with the simulator open you might want to use the Environment Overrides window instead.

How do I Turn Off dark mode for the user interface?

If you don’t have the time to add support for Dark mode you can simply disable it by adding the UIUserInterfaceStyle to your Info.plist and set it to Light. You can override the user interface style per view controller and set it to light or dark using the following code:

How do I enable and switch appearance mode?

There are multiple ways to enable and switch appearance mode that all have their benefits. Navigate to the Developer page in the Settings app on your simulator and turn on the switch for Dark Appearance: On a device, you can enable Dark Mode by navigating to the Display & Brightness page in the Settings app.

How do I know if a trait has dark mode?

Dark mode can be detected by using the userInterfaceStyle property on the current trait collection. When it’s set to dark you know that the current appearance is set to dark. When you use adaptive colors with CALayers you’ll notice that these colors are not updating when switching appearance live in the app.


3 Answers

You can override the style for single views or view controller using the overrideUserInterfaceStyle property. But since the window is also a view, you can set that on your main window to force it into light or dark mode:

window.overrideUserInterfaceStyle = .dark
like image 195
Frank Schlegel Avatar answered Oct 17 '22 18:10

Frank Schlegel


You can use one of the observation ways, for example, Defaults lib, and then add

window.overrideUserInterfaceStyle = .dark

to

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {}

method

like image 2
nastassia Avatar answered Oct 17 '22 18:10

nastassia


I want to elaborate more on the answer provided by @Frank Schlegel.

To change theme from another view controller in your app (which is what you originally asked for, I think) you could add an observer for a UserDefaults value that will trigger the change.

I would add an enum to better represent the theme state

enum Theme: String {
    case light, dark, system

    // Utility var to pass directly to window.overrideUserInterfaceStyle
    var uiInterfaceStyle: UIUserInterfaceStyle {
        switch self {
        case .light:
            return .light
        case .dark:
            return .dark
        case .system:
            return .unspecified
        }
    }
}

In your SceneDelegate under your window initialisation you have to add this method that is triggered every time UserDefaults changes value.

UserDefaults.standard.addObserver(self, forKeyPath: "theme", options: [.new], context: nil)

Also, you want to remove that observer when the SceneDelegate is deinitialised, add

deinit {
    UserDefaults.standard.removeObserver(self, forKeyPath: "theme", context: nil)
}

This will place an observer for that theme value in UserDefaults.

To handle changes you need to add this method to your SceneDelegate class.

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
    guard
        let change = change,
        object != nil,
        keyPath == Defaults.theme.rawValue,
        let themeValue = change[.newKey] as? String,
        let theme = Theme(rawValue: themeValue)?.uiInterfaceStyle
    else { return }

    UIView.animate(withDuration: 0.3, delay: 0.0, options: .curveLinear, animations: { [weak self] in
        self?.window?.overrideUserInterfaceStyle = theme
    }, completion: .none)
}

This will be executed every time theme value changes in UserDefaults and will animate the transition from a theme to another.

Now, to change your theme from other view controllers in your app you just need to change value for UserDefaults.

UserDefaults.standard.setValue(Theme.light.rawValue, forKey: "theme")
like image 1
Mattia Righetti Avatar answered Oct 17 '22 16:10

Mattia Righetti