TheCesco1988
TheCesco1988

Reputation: 309

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.

Upvotes: 27

Views: 24890

Answers (3)

nastassia
nastassia

Reputation: 897

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) {}

Upvotes: 4

Mattia Righetti
Mattia Righetti

Reputation: 1360

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")

Upvotes: 7

Frank Rupprecht
Frank Rupprecht

Reputation: 10408

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

Upvotes: 52

Related Questions