iosswiftswiftuiios13ios-darkmode

SwiftUI How to implement dark mode toggle and refresh all views


I'd like to implement a simple toggle to manually switch between dark and light modes. However, I don't know how to make my app refresh (redraw all windows) after I switch the toggle.

So far I found these answers Manually set light/dark mode in SwiftUI and Implement dark mode switch in SwiftUI App.

But both these solutions use SceneDelegate.shared which is not recommended according to this answer.

var isDark: Bool = true {
    didSet { SceneDelegate.shared?.window!.overrideUserInterfaceStyle = isDark ? .dark : .light }
}

Is there really no other option to implement this? I tried adding @Environment variable but it works only once when the application starts. And I need my app to update the color scheme on toggle change.

.environment(\.colorScheme, settings.isDarkMode ? .dark : .light)

This is my toggle:

struct SettingsView: View {
    var body: some View {
        Toggle(isOn: $settings.isDarkMode) {
            Text("Night mode")
        }
    }
}

In my model I have this variable:

@UserDefaultsBacked(key: UserDefaults.Keys.Settings.darkMode, defaultValue: false)
var isDarkMode: Bool

Solution

  • Just add .colorScheme to your top View and add a color scheme variable (@State, @Binding, etc.). Whenever the variable changes the view (and children) update automatically.

    struct ContentView: View {
        @State var theColorScheme: ColorScheme = .dark
    
        func toggleColorScheme() {
            theColorScheme = (theColorScheme == .dark) ? .light : .dark
        }
        
        var body: some View {
            ZStack { // or any other View
                Color.primary // to make the change visible
    
                Button(action: self.toggleColorScheme) {
                    Text("Toggle")
                }
            }   .colorScheme(theColorScheme)
        }
    }
    

    Note: if you want to update the color scheme of the whole presentation, you're better of using .preferredColorScheme instead of .colorScheme.