如何使用系统值选项实现配色方案切换?

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()
            }
        }
    }
}