Implement Dark Mode Switch in Swiftui App

Implement dark mode switch in SwiftUI App


Single View

To change the color scheme of a single view (Could be the main ContentView of the app), you can use the following modifier:

.environment(\.colorScheme, .light) // or .dark

or

.preferredColorScheme(.dark)

Also, you can apply it to the ContentView to make your entire app dark!

Assuming you didn't change the ContentView name in scene delegate or @main



Entire App (Including the UIKit parts and The SwiftUI)

First you need to access the window to change the app colorScheme that called UserInterfaceStyle in UIKit.

I used this in SceneDelegate:

private(set) static var shared: SceneDelegate?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
Self.shared = self
...
}

Then you need to bind an action to the toggle. So you need a model for it.

struct ToggleModel {
var isDark: Bool = true {
didSet {
SceneDelegate.shared?.window!.overrideUserInterfaceStyle = isDark ? .dark : .light
}
}
}

At last, you just need to toggle the switch:

struct ContentView: View {
@State var model = ToggleModel()

var body: some View {
Toggle(isOn: $model.isDark) {
Text("is Dark")
}
}
}


From the UIKit part of the app

Each UIView has access to the window, So you can use it to set the . overrideUserInterfaceStyle value to any scheme you need.

myView.window?.overrideUserInterfaceStyle = .dark

SwiftUI How to implement dark mode toggle and refresh all views

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.

Manually set light/dark mode in SwiftUI and save users choice

Here is possible approach (scratchy, you can find here on SO property wrapper for defaults and use it for better styling, but the idea to achieve your goal is the same)

Tested with Xcode 11.4 / iOS 13.4

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?

private(set) static var shared: SceneDelegate?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
Self.shared = self

let contentView = ContentView()

if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)

// restore from defaults initial or previously stored style
let style = UserDefaults.standard.integer(forKey: "LastStyle")
window.overrideUserInterfaceStyle = (style == 0 ? .dark : UIUserInterfaceStyle(rawValue: style)!)

window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}

...
}


struct ContentView: View {
var body: some View {
// manipulate with style directly in defaults
Toggle(isOn: Binding<Bool>(
get: { UserDefaults.standard.integer(forKey: "LastStyle") !=
UIUserInterfaceStyle.light.rawValue },
set: {
SceneDelegate.shared?.window!.overrideUserInterfaceStyle = $0 ? .dark : .light
UserDefaults.standard.setValue($0 ? UIUserInterfaceStyle.dark.rawValue : UIUserInterfaceStyle.light.rawValue, forKey: "LastStyle")
}
)) {
Text("is Dark")
}
}
}

SwiftUI: Force View to use light or dark mode

.colorScheme() is deprecated does not work with the background well.

.preferredColorScheme() is the way now. Does the job with the background too.

TestView1()
.preferredColorScheme(.dark)

TestView2()
.preferredColorScheme(.light)

Developer docs for .preferredColorScheme()

SwiftUI - Making a button suitable for light and dark mode

In you case you could simply do:

                Text("Button \(button)")
.padding(.vertical, 12.5)
.padding(.horizontal, 120)
.foregroundStyle(.background)
.background(2 == button ? Color.primary: Color.secondary)
.clipShape(Capsule())

or for more control use:

@Environment(\.colorScheme) var colorScheme

var textColor: Color {
if colorScheme == .dark {
return Color.white
} else {
return Color.black
}
}

or follow @loremipsum and define your own colors in Assets

Is there a way to change dark to light theme and vice versa just like using a toggle programmitically in swift UI?


WindowGroup {
switch currentThemeRawValue {
case Theme.dark.rawValue:
ContentView().environment(\.colorScheme, .dark)
case Theme.light.rawValue:
ContentView().environment(\.colorScheme, .light)
default:
ContentView().environment(\.colorScheme, .dark)
}
}

The switch statement means that SwiftUI is populating the view with conditional content, so it would rebuild the entire hierarchy if the value changes. You only really want to change the environment value itself, so something like this would probably be better:

WindowGroup {
ContentView()
.environment(\.colorScheme, currentThemeRawValue == "dark" ?? .dark : .light)
}

SwiftUI SignInWithAppleButton dark mode

For some reason the button doesn't update automatically with the color scheme, but you can fix it with the code below:

struct AuthenticationView: View {
@Environment(\.colorScheme) private var colorScheme
let authManager = AuthManager()

private var buttonStyle: SignInWithAppleButton.Style {
switch colorScheme {
case .light: return .black
case .dark: return .white
@unknown default: return .black
}
}

var body: some View {
SignInWithAppleButton(
.signIn,
onRequest: authManager.createRequest,
onCompletion: authManager.handleResult
)
.signInWithAppleButtonStyle(buttonStyle)
.id(colorScheme)
.frame(height: 56)
}
}

SwiftUI dark / light mode change with @AppStorage

Why would you make the app responsible for the dark and light theme, I think you should let the system handle this. Other then that check your function. If you call ContentView it makes sense that it is shown.

And if you don't want the system to handle this. check this post How to switch programmatically to dark mode swift maybe this helps you

SwiftUI: How to let the user set the app appearance in real-time w/ options light, dark, and system?

I ended up using the following solution which is a slight adaptation of the answer that @pgb gave:

ContentView:

struct ContentView: View {

@AppStorage("selectedAppearance") var selectedAppearance = 0
var utilities = Utilities()

var body: some View {
VStack {
Spacer()
Button(action: {
selectedAppearance = 1
}) {
Text("Light")
}
Spacer()
Button(action: {
selectedAppearance = 2
}) {
Text("Dark")
}
Spacer()
Button(action: {
selectedAppearance = 0
}) {
Text("System")
}
Spacer()
}
.onChange(of: selectedAppearance, perform: { value in
utilities.overrideDisplayMode()
})
}
}

Helper class

class Utilities {

@AppStorage("selectedAppearance") var selectedAppearance = 0
var userInterfaceStyle: ColorScheme? = .dark

func overrideDisplayMode() {
var userInterfaceStyle: UIUserInterfaceStyle

if selectedAppearance == 2 {
userInterfaceStyle = .dark
} else if selectedAppearance == 1 {
userInterfaceStyle = .light
} else {
userInterfaceStyle = .unspecified
}

UIApplication.shared.windows.first?.overrideUserInterfaceStyle = userInterfaceStyle
}
}


Related Topics



Leave a reply



Submit