1

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
pawello2222
  • 46,897
  • 22
  • 145
  • 209

2 Answers2

1

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.

Jack Goossen
  • 799
  • 4
  • 14
  • This code does not work on Xcode 13. – Tom Schulz Nov 03 '21 at 05:06
  • @TomSchulz Just verified it still works with Xcode 13, but the example was very silly; the (original) VStack didn't contain any view that visibly changed with the colorScheme. I updates the example accordingly. – Jack Goossen Nov 08 '21 at 13:49
1

This solution works for me

Step 1 :

struct AppName: App {

@AppStorage("appTheme") private var isDarkModeOn = false

var body: some Scene {
    WindowGroup {
        MainTabView()
            .preferredColorScheme(isDarkModeOn ? .dark : .light)
    }
}}

Step 2 :

Then you change this variable via button click

var body: some View {

@AppStorage("appTheme") private var isDarkModeOn = false

    VStack {
            Button {
                self.isDarkMode.toggle()
            } label: {
                Image("dark_mode_icon")
            }
      }}