如何使用系统值选项实现配色方案切换?
How to implement a color scheme switch with the system value option?
我已经使用此处关于此 的指南在我的应用程序中实施了 dark/light 模式切换。示例代码如下:
public struct DarkModeViewModifier: ViewModifier {
@AppStorage("isDarkMode") var isDarkMode: Bool = true
public func body(content: Content) -> some View {
content
.environment(\.colorScheme, isDarkMode ? .dark : .light)
.preferredColorScheme(isDarkMode ? .dark : .light) // tint on status bar
}
}
并调用它:
Picker("Color", selection: $isDarkMode) {
Text("Light").tag(false)
Text("Dark").tag(true)
}
.pickerStyle(SegmentedPickerStyle())
如何通过添加 System
段来实现这一点?我想将 Int
设置为默认设置,但我不知道如何将它与 @AppStorage
属性 包装器联系起来。
以及在 SwiftUI 中,监视系统模式更改如何生效?
更新:在 iOS 15 中,windows 似乎已被弃用。如何以最理智的方式为 iOS 15 更新它?我已经看到了 isKeyWindow 的一些其他解决方案,但不确定如何在此处应用它。
为此,您需要将用户的显示偏好从 Bool 存储到自定义枚举。然后,从这个自定义枚举中,您可以确定外观应该是深色还是浅色,并基于此应用显示首选项。
示例代码:
struct ContentView: View {
enum DisplayMode: Int {
case system = 0
case dark = 1
case light = 2
}
@AppStorage("displayMode") var displayMode: DisplayMode = .system
func overrideDisplayMode() {
var userInterfaceStyle: UIUserInterfaceStyle
switch displayMode {
case .dark: userInterfaceStyle = .dark
case .light: userInterfaceStyle = .light
case .system: userInterfaceStyle = UITraitCollection.current.userInterfaceStyle
}
UIApplication.shared.windows.first?.overrideUserInterfaceStyle = userInterfaceStyle
}
var body: some View {
VStack {
Picker("Color", selection: $displayMode) {
Text("System").tag(DisplayMode.system)
Text("Light").tag(DisplayMode.light)
Text("Dark").tag(DisplayMode.dark)
}
.pickerStyle(SegmentedPickerStyle())
.onReceive([self.displayMode].publisher.first()) { _ in
overrideDisplayMode()
}
}.onAppear(perform: overrideDisplayMode)
}
}
基本上,你在做的是
- 为每个显示模式分配一个整数值(因此它可以存储在@AppStorage 中)
- 设置选择器以在系统、深色和浅色之间进行选择,并将值保存在 UserDefaults 中
- 通过打开@AppStorage 值来确定应用程序是否处于黑暗模式
- 使用
UIApplication.shared.windows.first?.overrideInterfaceStyle
通过视图和子视图传递自定义暗模式配置
感谢@diogo 的解决方案。我已经将它调整为 ios 15 可以在设置页面中使用的自定义视图:
struct DisplayModeSetting: View {
enum DisplayMode: Int {
case system, dark, light
var colorScheme: ColorScheme? {
switch self {
case .system: return nil
case .dark: return ColorScheme.dark
case .light: return ColorScheme.light
}
}
func setAppDisplayMode() {
var userInterfaceStyle: UIUserInterfaceStyle
switch self {
case .system: userInterfaceStyle = UITraitCollection.current.userInterfaceStyle
case .dark: userInterfaceStyle = .dark
case .light: userInterfaceStyle = .light
}
let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene
scene?.keyWindow?.overrideUserInterfaceStyle = userInterfaceStyle
}
}
@AppStorage("displayMode") var displayMode = DisplayMode.system
var body: some View {
HStack {
Text("Display mode:")
Picker("Is Dark?", selection: $displayMode) {
Text("System").tag(DisplayMode.system)
Text("Dark").tag(DisplayMode.dark)
Text("Light").tag(DisplayMode.light)
}
.pickerStyle(SegmentedPickerStyle())
.onChange(of: displayMode) { newValue in
print(displayMode)
displayMode.setAppDisplayMode()
}
}
}
}
我已经使用此处关于此
public struct DarkModeViewModifier: ViewModifier {
@AppStorage("isDarkMode") var isDarkMode: Bool = true
public func body(content: Content) -> some View {
content
.environment(\.colorScheme, isDarkMode ? .dark : .light)
.preferredColorScheme(isDarkMode ? .dark : .light) // tint on status bar
}
}
并调用它:
Picker("Color", selection: $isDarkMode) {
Text("Light").tag(false)
Text("Dark").tag(true)
}
.pickerStyle(SegmentedPickerStyle())
如何通过添加 System
段来实现这一点?我想将 Int
设置为默认设置,但我不知道如何将它与 @AppStorage
属性 包装器联系起来。
以及在 SwiftUI 中,监视系统模式更改如何生效?
更新:在 iOS 15 中,windows 似乎已被弃用。如何以最理智的方式为 iOS 15 更新它?我已经看到了 isKeyWindow 的一些其他解决方案,但不确定如何在此处应用它。
为此,您需要将用户的显示偏好从 Bool 存储到自定义枚举。然后,从这个自定义枚举中,您可以确定外观应该是深色还是浅色,并基于此应用显示首选项。
示例代码:
struct ContentView: View {
enum DisplayMode: Int {
case system = 0
case dark = 1
case light = 2
}
@AppStorage("displayMode") var displayMode: DisplayMode = .system
func overrideDisplayMode() {
var userInterfaceStyle: UIUserInterfaceStyle
switch displayMode {
case .dark: userInterfaceStyle = .dark
case .light: userInterfaceStyle = .light
case .system: userInterfaceStyle = UITraitCollection.current.userInterfaceStyle
}
UIApplication.shared.windows.first?.overrideUserInterfaceStyle = userInterfaceStyle
}
var body: some View {
VStack {
Picker("Color", selection: $displayMode) {
Text("System").tag(DisplayMode.system)
Text("Light").tag(DisplayMode.light)
Text("Dark").tag(DisplayMode.dark)
}
.pickerStyle(SegmentedPickerStyle())
.onReceive([self.displayMode].publisher.first()) { _ in
overrideDisplayMode()
}
}.onAppear(perform: overrideDisplayMode)
}
}
基本上,你在做的是
- 为每个显示模式分配一个整数值(因此它可以存储在@AppStorage 中)
- 设置选择器以在系统、深色和浅色之间进行选择,并将值保存在 UserDefaults 中
- 通过打开@AppStorage 值来确定应用程序是否处于黑暗模式
- 使用
UIApplication.shared.windows.first?.overrideInterfaceStyle
通过视图和子视图传递自定义暗模式配置
感谢@diogo 的解决方案。我已经将它调整为 ios 15 可以在设置页面中使用的自定义视图:
struct DisplayModeSetting: View {
enum DisplayMode: Int {
case system, dark, light
var colorScheme: ColorScheme? {
switch self {
case .system: return nil
case .dark: return ColorScheme.dark
case .light: return ColorScheme.light
}
}
func setAppDisplayMode() {
var userInterfaceStyle: UIUserInterfaceStyle
switch self {
case .system: userInterfaceStyle = UITraitCollection.current.userInterfaceStyle
case .dark: userInterfaceStyle = .dark
case .light: userInterfaceStyle = .light
}
let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene
scene?.keyWindow?.overrideUserInterfaceStyle = userInterfaceStyle
}
}
@AppStorage("displayMode") var displayMode = DisplayMode.system
var body: some View {
HStack {
Text("Display mode:")
Picker("Is Dark?", selection: $displayMode) {
Text("System").tag(DisplayMode.system)
Text("Dark").tag(DisplayMode.dark)
Text("Light").tag(DisplayMode.light)
}
.pickerStyle(SegmentedPickerStyle())
.onChange(of: displayMode) { newValue in
print(displayMode)
displayMode.setAppDisplayMode()
}
}
}
}