Reputation: 309
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
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
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
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