【问题标题】:Manually set light/dark mode in SwiftUI and save users choice在 SwiftUI 中手动设置明暗模式并节省用户选择
【发布时间】:2025-12-01 22:30:02
【问题描述】:

我在这里找到的另一个线程中找到了在 swiftui 应用程序中手动设置明/暗模式的解决方案 https://*.com/a/58476468/11698443 它主要工作,但有两个问题。

  1. 用户的选择不会永久保存。

  2. 我希望默认选择为暗模式,因此无论用户将系统设置为亮模式还是暗模式,应用程序最初都会以暗模式显示。

目前,这个实现有点小错误,因为如果用户在灯光模式下打开应用并点击切换开关。他们第一次按下开关不会做任何事情。他们必须再按两次开关才能触发 didSet 以使应用程序进入暗模式,即使这样,选择也不会被保存。

其他一些线程询问暗模式实现,但大多数处理 UIKit 并且我上面链接的线程是我可以主要在 swiftui 中工作的唯一解决方案。是否可以修改该解决方案以解决我提出的两个问题?

【问题讨论】:

  • 老兄什么......让操作系统处理它
  • 人们在系统上设置亮或暗模式,并期望它受到您的应用程序的尊重。您的应用应该与设置明暗模式无关。
  • 如果您的应用程序只适用于灯光模式怎么办?视力受损的原因,或者您正在尝试显示在暗模式下不显示的图形...

标签: ios swift swiftui


【解决方案1】:

这是一种可能的方法(很粗糙,您可以在 SO 属性包装器上找到默认值并将其用于更好的样式,但实现目标的想法是相同的)

使用 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")
        }
    }
}

【讨论】:

  • 这如何与没有 SceneDelegate 或窗口的 iOS 14 一起工作(如果您有一个仅 iOS14+ 的应用程序)?有没有办法将它应用到 WindowGroup{}?
  • 我也有同样的问题。找不到符合“新”Apple 方式并在适当时使用轻量模式的方式。
  • 感谢它和我一起正常工作@PStamatiou @addzo 即使您以 SwiftUI App life circle 启动您的应用程序,您也可以创建新项目作为 UIkit 委托然后删除 ProjectApp 文件(该文件有 @ main) 然后用AppDelegateSceneDelegate 替换它,然后转到Info.plist 并比较带有AppDelagte 的新项目和您当前的项目,你会发现一些价值改变它,你很高兴!
【解决方案2】:

PStamatiou.

当我不使用场景委托并且只有内容视图时,这对我有用:

@main
struct YourApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.colorScheme, .light)
        }
    }
}

【讨论】: