In my app I allow the user to override the appearance that is set on the device. Here's the class that handles updating the UI if they want to override the system-wide appearance:
class UserInterfaceStyleController {
init() {
self.handleUserInterfaceStyleChange()
NotificationCenter.default.addObserver(self, selector: #selector(self.handleUserInterfaceStyleChange), name: Notification.Name(OMGNotification.changeBizzaroMode.rawValue), object: nil)
}
@objc func handleUserInterfaceStyleChange() {
if #available(iOS 13.0, *) {
let windows = UIApplication.shared.windows
for window in windows {
if UIScreen.main.traitCollection.userInterfaceStyle == .light {
if UserDefaults.standard.bizzaroMode {
window.overrideUserInterfaceStyle = .dark
} else {
window.overrideUserInterfaceStyle = .light
}
}
if UIScreen.main.traitCollection.userInterfaceStyle == .dark {
if UserDefaults.standard.bizzaroMode {
window.overrideUserInterfaceStyle = .light
} else {
window.overrideUserInterfaceStyle = .dark
}
}
window.subviews.forEach({ view in
view.layoutIfNeeded()
})
}
}
}
This works - the user flips the switch and the app changes the userInterfaceStyle. The problem is the app doesn't change appearance automatically when the user changes the system-wide appearance (set light or dark mode in settings), ONLY when they set it manually.
So I'm assuming I need to do some work when the user changes it system-wide, and traitCollectionDidChange seems to be what I need to use. Problem is it doesn't fire when the user changes the appearance in settings, ONLY when it's manually set in my app. I've got this code in a viewController to test that:
class ViewController: UIViewController, UIGestureRecognizerDelegate {
// Lots of stuff
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
print("We have a style mode change")
}
// Lots more stuff
}
When I manually override, it prints, "We have a style mode change". When I go into settings and switch the system-wide appearance traitCollectionDidChange doesn't fire.
I swear I had this working fine at one point, but I've been trying to fix some weird issues for a while now when I use overrideUserInterfaceStyle and have burned through a lot of code changes.
I think I'm missing something obvious. Before I started allowing the user to override, the app switched automatically in the background when the appearance was changed system-wide with no code at all. Now it doesn't and traitCollectionDidChange doesn't fire. What am I missing or doing wrong? Happy to provide more code if it could help.
Thanks!
The problem is that you override the user interface style, which causes any system changes not to become propagated to your window. You need to set overrideUserInterfaceStyle
to .unspecified
, otherwise the system won't call the traitCollectionDidChange
methods in your view stack on change.
In our apps, we offer the user three options: automatic, light and dark, where each option just sets overrideUserInterfaceStyle
to the appropriate option.
By the way, you don't need to call layoutIfNeeded()
on all your subviews—setting overrideUserInterfaceStyle
triggers a redraw automatically.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With