Force color scheme at the press of a button

This is once again about that Lunch Card app.

There's a section in my app's settings that controls the appearance - color scheme, if you will - of the whole app. The three options are
  1. System Default

  2. Light

  3. Dark

The picker is set up, and it works as normal, but what would I specify the action of these buttons to be to force a certain color scheme, whether that be dark, light, or auto.

Here's the code for where the picker is:
Code Block
struct SettingsView: View {
    @State private var selectedAppearance = 1
    var body: some View {
// ...
Picker(selection: $selectedAppearance, label: Text("Appearance")) {
Button(action: {
// Change app color scheme to be auto
}) {
Text("System Default")
}.tag(1)
Button(action: {
// Change app color scheme to be light
}) {
Text("Light")
}.tag(2)
Button(action: {
// Change app color scheme to be dark
}) {
Text("Dark")
}.tag(3)
}
// ...
}

Accepted Reply

Thanks for showing your code. Seems onAppear is called more times than expected with your view structure.

Please try this.

SettingsView:

Code Block
import SwiftUI
struct SettingsView: View {
@State private var selectedAppearance = 1
@Binding var colorScheme: ColorScheme?
var body: some View {
NavigationView {
Form {
Section(header: Text("Customization")) {
Picker(selection: $selectedAppearance, label: Text("Appearance")) {
Text("System Default").tag(1)
Text("Light").tag(2)
Text("Dark").tag(3)
}
.onChange(of: selectedAppearance) { value in
print(selectedAppearance)
switch selectedAppearance {
case 1:
//print("System Default")
colorScheme = nil
case 2:
//print("Light")
colorScheme = .light
case 3:
//print("Dark")
colorScheme = .dark
default:
break
}
}
//...
}
//...
}
} //<- Closing `NavigationView`
.onAppear {
switch colorScheme {
case .none:
selectedAppearance = 1
case .light:
selectedAppearance = 2
case .dark:
selectedAppearance = 3
default:
break
}
}
}
}


Replies

You should create an environment var in ContentView
https://stackoverflow.com/questions/60368765/how-to-change-tintcolor-of-whole-app-swiftui

And use it to change the color them. This could help
https://stackoverflow.com/questions/58476048/implement-dark-mode-switch-in-swiftui-app

This could help

I probably should've been more clear.
I took a glance at these and saw that they weren't using the SwiftUI Lifecycle (No App/SceneDelegates).
It's possible that I might've missed something, and correct me if I'm wrong.
You're right.

You just forgot to tell… 😉
You can override system color scheme using the view modifier colorScheme:
Code Block
import SwiftUI
struct ContentView: View {
@Environment(\.colorScheme) var systemColorScheme
@State var myColorScheme: ColorScheme?
var body: some View {
NavigationView {
VStack {
Text("Hello, world!")
//...
NavigationLink("Settings", destination: SettingsView(colorScheme: $myColorScheme))
}
}
//Override `colorScheme`
.colorScheme(myColorScheme ?? systemColorScheme)
}
}
struct SettingsView: View {
@State private var selectedAppearance = 1
@Binding var colorScheme: ColorScheme?
var body: some View {
// ...
Picker(selection: $selectedAppearance, label: Text("Appearance")) {
Text("System Default").tag(1)
Text("Light").tag(2)
Text("Dark").tag(3)
}
.onAppear {
switch colorScheme {
case .none:
selectedAppearance = 1
case .light:
selectedAppearance = 2
case .dark:
selectedAppearance = 3
default:
break
}
}
.onChange(of: selectedAppearance) { value in
//print(selectedAppearance)
switch selectedAppearance {
case 1:
//print("System Default")
colorScheme = nil
case 2:
//print("Light")
colorScheme = .light
case 3:
//print("Dark")
colorScheme = .dark
default:
break
}
}
// ...
}
}


  • Any idea if there is any other way we can change the full app color scheme in an easier/simpler way ? I have various structs in between my @main struct and the struct that's actually changing the color scheme and I would like to avoid doing multiple @Bindings, @State, @Environment, etc

Add a Comment
So I tried this out, and I have a feeling it would work if $selectedAppearance actually changed. I added the new code but now when I try to select Light or Dark, it just dismisses and defaults to System Default. I may have missed something, so please acknowledge that.

please acknowledge that.

OK. Please show your latest code if you cannot solve the issue yourself.

Please show your latest code if you cannot solve the issue yourself.

ContentView:
Code Block
//
//  ContentView.swift
//  Shared
//
//  Created by Joshua Srery on 12/17/20.
//  Additional code by OOPer on Apple Developer Forums
//
import SwiftUI
struct ContentView: View {
    @Environment(\.colorScheme) var systemColorScheme
    @State var myColorScheme: ColorScheme?
    var body: some View {
        TabView {
            CardsView()
                .tabItem {
                    Image(systemName: "person.crop.square.fill.and.at.rectangle")
                    Text("Cards")
                }
            SettingsView(colorScheme: $myColorScheme)
                .tabItem {
                    Image(systemName: "gear")
                    Text("Settings")
                }
        }
        .colorScheme(myColorScheme ?? systemColorScheme)
    }
}


SettingsView:
Code Block
//
//  SettingsView.swift
//  Lunch Card (iOS)
//
//  Created by Joshua Srery on 12/21/20.
//  Additional code by OOPer on Apple Developer Forums.
//
import SwiftUI
struct SettingsView: View {
    @State private var selectedAppearance = 1
    @Binding var colorScheme: ColorScheme?
    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("Customization")) {
                    Picker(selection: $selectedAppearance, label: Text("Appearance")) {
                        Text("System Default").tag(1)
                        Text("Light").tag(2)
                        Text("Dark").tag(3)
                    }
                    .onAppear {
                        switch colorScheme {
                        case .none:
                            selectedAppearance = 1
                        case .light:
                            selectedAppearance = 2
                        case .dark:
                            selectedAppearance = 3
                        default:
                            break
                        }
                    }
                    .onChange(of: selectedAppearance) { value in
                        //print(selectedAppearance)
                        switch selectedAppearance {
                        case 1:
                            //print("System Default")
                            colorScheme = nil
                        case 2:
                            //print("Light")
                            colorScheme = .light
                        case 3:
                            //print("Dark")
                            colorScheme = .dark
                        default:
                            break
                        }
                    }
///...
}

Thanks for showing your code. Seems onAppear is called more times than expected with your view structure.

Please try this.

SettingsView:

Code Block
import SwiftUI
struct SettingsView: View {
@State private var selectedAppearance = 1
@Binding var colorScheme: ColorScheme?
var body: some View {
NavigationView {
Form {
Section(header: Text("Customization")) {
Picker(selection: $selectedAppearance, label: Text("Appearance")) {
Text("System Default").tag(1)
Text("Light").tag(2)
Text("Dark").tag(3)
}
.onChange(of: selectedAppearance) { value in
print(selectedAppearance)
switch selectedAppearance {
case 1:
//print("System Default")
colorScheme = nil
case 2:
//print("Light")
colorScheme = .light
case 3:
//print("Dark")
colorScheme = .dark
default:
break
}
}
//...
}
//...
}
} //<- Closing `NavigationView`
.onAppear {
switch colorScheme {
case .none:
selectedAppearance = 1
case .light:
selectedAppearance = 2
case .dark:
selectedAppearance = 3
default:
break
}
}
}
}


Works...thank you so much!