Skip to content

Instantly share code, notes, and snippets.

@ryanlintott
Last active April 23, 2024 09:54
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save ryanlintott/0bb21cd36a55f519bda5b441736edefe to your computer and use it in GitHub Desktop.
Save ryanlintott/0bb21cd36a55f519bda5b441736edefe to your computer and use it in GitHub Desktop.
Custom color scheme view modifier for SwiftUI. Sets the color scheme to light mode, dark mode, or matches the system. If set to match, the app will respond to system-wide color scheme changes properly. A simple widget color scheme implementation is also provided.
import SwiftUI
enum CustomColorScheme: Int, CaseIterable, Identifiable, Codable {
static var defaultKey = "customColorScheme"
static var defaultValue = CustomColorScheme.system
case system = 0
case light = 1
case dark = 2
var id: Int {
self.rawValue
}
var colorScheme: ColorScheme? {
switch self {
case .system:
return nil
case .light:
return .light
case .dark:
return .dark
}
}
var label: String {
switch self {
case .system:
return "System"
case .light:
return "Light"
case .dark:
return "Dark"
}
}
}
import SwiftUI
struct CustomColorSchemeExampleView: View {
// Store custom color scheme in UserDefaults
@AppStorage(CustomColorScheme.defaultKey) var customColorScheme = CustomColorScheme.defaultValue
var body: some View {
Text("Hello World")
.customColorScheme($customColorScheme)
}
}
import SwiftUI
struct CustomColorSchemeViewModifier: ViewModifier {
// This variable holds the currently active colour scheme (changed with calls to preferredColorScheme)
@Environment(\.colorScheme) var colorScheme
@Binding var customColorScheme: CustomColorScheme
// Temp value used for changing colorScheme when switching customColorScheme to .system
@State private var tempColorScheme: ColorScheme? = nil
init(_ customColorScheme: Binding<CustomColorScheme>) {
self._customColorScheme = customColorScheme
}
// This function is required to get the system color scheme
func getSystemColorScheme() -> ColorScheme {
return UITraitCollection.current.userInterfaceStyle == .light ? .light : .dark
}
func body(content: Content) -> some View {
content
.preferredColorScheme(tempColorScheme ?? customColorScheme.colorScheme)
.onChange(of: customColorScheme) { value in
if value == .system {
tempColorScheme = getSystemColorScheme()
}
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in
if customColorScheme == .system {
let systemColorScheme = getSystemColorScheme()
if systemColorScheme != colorScheme {
tempColorScheme = systemColorScheme
}
}
}
.onChange(of: tempColorScheme) { value in
if value != nil {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
// Resets tempColorScheme back to nil. This occurs after colorScheme has been updated
tempColorScheme = nil
}
}
}
}
}
extension View {
func customColorScheme(_ customColorScheme: Binding<CustomColorScheme>) -> some View {
self.modifier(CustomColorSchemeViewModifier(customColorScheme))
}
}
import SwiftUI
import WidgetKit
struct WidgetCustomColorSchemeExampleView: View {
// Use an app group to store and let users change it by referencing the same variable in your app.
@AppStorage(CustomColorScheme.defaultKey, store: "com.your.groupstore") var customColorScheme = CustomColorScheme.defaultValue
var body: some View {
Text("Hello World")
.widgetCustomColorScheme(customColorScheme)
}
}
import SwiftUI
import WidgetKit
struct WidgetCustomColorSchemeViewModifier: ViewModifier {
// As we're not changing the preferred color scheme we can trust this to always be the system value
@Environment(\.colorScheme) var colorScheme
let customColorScheme: CustomColorScheme
init(_ customColorScheme: CustomColorScheme) {
self.customColorScheme = customColorScheme
}
func body(content: Content) -> some View {
content
// .preferredColorScheme doesn't work on widgets
.colorScheme(customColorScheme.colorScheme ?? colorScheme)
}
}
extension View {
func widgetCustomColorScheme(_ customColorScheme: CustomColorScheme) -> some View {
self.modifier(WidgetCustomColorSchemeViewModifier(customColorScheme))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment