Rizwan's Blog 👨‍💻

SwiftUI Color Scheme

July 22, 2020

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.

Links