avatar

SwiftUI Color Scheme


In UIKit app, If you would like to change the color scheme within the app. Setting UserInterfaceStyle object to your root window would help.

Take a look at NetNewsWire iOS app. It gives Appearance settings where you can choose your color palette for the app. These are the possible options

  • Automatic: respects system color scheme and changes whenever user switching between light and dark mode from the OS
  • Light: Light mode for the whole app and didn’t respect the system color scheme
  • Dark: dark mode for the whole app and didn’t respect the system color scheme
func updateUserInterfaceStyle() {
  switch AppDefaults.userInterfaceColorPalette {
    case .automatic:
      window!.overrideUserInterfaceStyle = .unspecified
    case .light:
      window!.overrideUserInterfaceStyle = .light
    case .dark:
      window!.overrideUserInterfaceStyle = .dark
  }
}

Setting unspecified makes the app respects the system-wide color scheme here.

SwiftUI Way

SwiftUI gives us an environment variable and preferred color scheme modifier to change the color scheme.

struct ContentView: View {
    @State var preferredColorScheme: ColorScheme? = nil

    var body: some View {
        List {
		        Button(action: {
                preferredColorScheme = .light
            }) {
                HStack {
                    Text("Light")
                    Spacer()
                    if preferredColorScheme == .light {
                        selectedImage
                    }
                }
            }

            Button(action: {
                preferredColorScheme = .dark
            }) {
                HStack {
                    Text("Dark")
                    Spacer()
                    if preferredColorScheme == .dark {
                        selectedImage
                    }
                }
            }
        }
        .listStyle(InsetGroupedListStyle())
        .preferredColorScheme(preferredColorScheme)
        .navigationBarTitle("ColorScheme Test")
    }

    var selectedImage: some View {
        Image(systemName: "checkmark")
            .foregroundColor(.blue)
    }
}

In the above code, we are setting the preferredColorScheme modifier to our content view and changing it via @State object. It will give us one view with two options where you can change the color scheme.

It’s also possible with the environment value like below

@main
struct ColorSchemeTestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.colorScheme, .dark)
        }
    }
}

We can also get to know the currently selected color scheme via environment object like below.

@Environment(\.colorScheme) var colorScheme

NOTE

As of beta 2, there is no way that I can find, to set the colorScheme to unspecified to get automatic behaviour. I have even filed a Feedback. In case if you find a way to achieve it, let me know at twitter

I did create a sample project to explain the behaviour and you can find it here at Github.