在 SwiftUI App 中实现暗模式切换

Lil*_*aen 15 ios swift swiftui ios-darkmode

我目前正在我的应用程序中研究暗模式。虽然由于我的 SwiftUI 基础,暗模式本身并没有太大的困难,但我正在努力选择将 ColorScheme 设置为独立于系统 ColorScheme 的选项。

我在苹果人机界面指南中找到了这个,我想实现这个功能。(链接:人机界面指南

知道如何在 SwiftUI 中做到这一点吗?我发现了一些@Environment关于这个主题的提示,但没有进一步的信息。(链接:最后一段

Moj*_*ini 28

单一视图

要更改单个视图的配色方案(可能是ContentView应用程序的主视图),您可以使用以下修饰符:

.environment(\.colorScheme, .light) // or .dark
Run Code Online (Sandbox Code Playgroud)

或者

.preferredColorScheme(.dark)
Run Code Online (Sandbox Code Playgroud)

此外,您可以将其ContentView应用到 使您的整个应用程序变暗!

假设您没有更改ContentView场景委托中的名称或@main


整个应用程序(包括UIKit部分和SwiftUI

首先,您需要访问窗口以更改UserInterfaceStyleUIKit.

我在SceneDelegate

private(set) static var shared: SceneDelegate?

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

然后你需要将一个动作绑定到切换。所以你需要一个模型。

struct ToggleModel {
    var isDark: Bool = true {
        didSet { 
            SceneDelegate.shared?.window!.overrideUserInterfaceStyle = isDark ? .dark : .light 
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,您只需要切换开关:

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

     var body: some View {
         Toggle(isOn: $model.isDark) {
             Text("is Dark")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

来自应用程序的 UIKit 部分

每个人UIView都可以访问窗口,因此您可以使用它来将. overrideUserInterfaceStyle值设置为您需要的任何方案。

myView.window?.overrideUserInterfaceStyle = .dark
Run Code Online (Sandbox Code Playgroud)

  • 好吧,我不必问另一个问题,解决方案是第三种情况: `.unspecified` 。非常感谢您,祝您度过愉快的一天! (2认同)

hst*_*tdt 9

@AppStorage使用切换深色模式的demo

PS:对于全局切换,需要在WindowGroup/MainContentView上添加修饰符

import SwiftUI

struct SystemColor: Hashable {
    var text: String
    var color: Color
}

let backgroundColors: [SystemColor] = [.init(text: "Red", color: .systemRed), .init(text: "Orange", color: .systemOrange), .init(text: "Yellow", color: .systemYellow), .init(text: "Green", color: .systemGreen), .init(text: "Teal", color: .systemTeal), .init(text: "Blue", color: .systemBlue), .init(text: "Indigo", color: .systemIndigo), .init(text: "Purple", color: .systemPurple), .init(text: "Pink", color: .systemPink), .init(text: "Gray", color: .systemGray), .init(text: "Gray2", color: .systemGray2), .init(text: "Gray3", color: .systemGray3), .init(text: "Gray4", color: .systemGray4), .init(text: "Gray5", color: .systemGray5), .init(text: "Gray6", color: .systemGray6)]

struct DarkModeColorView: View {

    @AppStorage("isDarkMode") var isDarkMode: Bool = true

    var body: some View {
        Form {
            Section(header: Text("Common Colors")) {
                ForEach(backgroundColors, id: \.self) {
                    ColorRow(color: $0)
                }
            }
        }
        .toolbar {
            ToolbarItem(placement: .principal) { // navigation bar
               Picker("Color", selection: $isDarkMode) {
                    Text("Light").tag(false)
                    Text("Dark").tag(true)
                }
                .pickerStyle(SegmentedPickerStyle())
            }
        }
        .modifier(DarkModeViewModifier())
    }
}

private struct ColorRow: View {

    let color: SystemColor

    var body: some View {
        HStack {
            Text(color.text)
            Spacer()
            Rectangle()
                .foregroundColor(color.color)
                .frame(width: 30, height: 30)
        }
    }
}

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

struct DarkModeColorView_Previews: PreviewProvider {
    static var previews: some View {
        NavigationView {
            DarkModeColorView()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述


Art*_*uro 7

@ADB 的答案很好,但我找到了更好的答案。希望有人找到比我更好的:D 一旦应用程序切换状态(进入后台并返回),这种方法不会一遍又一遍地调用相同的函数

在你@main看来添加:

ContentView()
    .modifier(DarkModeViewModifier())
Run Code Online (Sandbox Code Playgroud)

现在创建DarkModeViewModifier()视图模型:

class AppThemeViewModel: ObservableObject {
    
    @AppStorage("isDarkMode") var isDarkMode: Bool = true                           // also exists in DarkModeViewModifier()
    @AppStorage("appTintColor") var appTintColor: AppTintColorOptions = .indigo
    
}

struct DarkModeViewModifier: ViewModifier {
    @ObservedObject var appThemeViewModel: AppThemeViewModel = AppThemeViewModel()
    
    public func body(content: Content) -> some View {
        content
            .preferredColorScheme(appThemeViewModel.isDarkMode ? .dark : appThemeViewModel.isDarkMode == false ? .light : nil)
            .accentColor(Color(appThemeViewModel.appTintColor.rawValue))
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 7

使用@AppStorage

初始应用程序启动代码

确保appearanceSwitch是一个可选的ColorScheme?所以.none不能被选择

import SwiftUI

@main
struct AnOkApp: App {

    @AppStorage("appearanceSelection") private var appearanceSelection: Int = 0

    var appearanceSwitch: ColorScheme? {
        if appearanceSelection == 1 {
            return .light
        }
        else if appearanceSelection == 2 {
            return .dark
        }
        else {
            return .none
        }
    }
    
    var body: some Scene {
        WindowGroup {
            AppearanceSelectionView()
                .preferredColorScheme(appearanceSwitch)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

选择视图

import SwiftUI

struct AppearanceSelectionView: View {

    @AppStorage("appearanceSelection") private var appearanceSelection: Int = 0
    
    var body: some View {
        NavigationView {
            Picker(selection: $appearanceSelection) {
                Text("System")
                    .tag(0)
                Text("Light")
                    .tag(1)
                Text("Dark")
                    .tag(2)
            } label: {
              Text("Select Appearance")
            }
            .pickerStyle(.menu)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


ADB*_*ADB 5

@Mojtaba Hosseini 的回答确实帮助了我,但我使用的是 iOS14@main而不是SceneDelegate,以及一些UIKit视图,所以我最终使用了这样的东西(这不会切换模式,但它确实设置了暗模式SwiftUIUIKit

@main
struct MyTestApp: App {

    @Environment(\.scenePhase) private var phase

    var body: some Scene {
        WindowGroup {
            ContentView()
                .accentColor(.red)
                .preferredColorScheme(.dark)
        }
        .onChange(of: phase) { _ in
            setupColorScheme()
        }
    }

    private func setupColorScheme() {
        // We do this via the window so we can access UIKit components too.
        let window = UIApplication.shared.windows.first
        window?.overrideUserInterfaceStyle = .dark
        window?.tintColor = UIColor(Color.red)
    }
}
Run Code Online (Sandbox Code Playgroud)