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)
}
// ...
}

Answered by OOPer in 655307022
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
}
}
}
}


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
}
}
// ...
}
}


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
                        }
                    }
///...
}

Accepted Answer
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!
Force color scheme at the press of a button
 
 
Q