向 iOS 应用添加深色模式

Adding dark mode to iOS app

我正在尝试为我的应用程序添加一个主题(深色主题)。因此,当用户单击 activity 开关时,它会使整个应用程序进入黑暗模式。我已经对黑暗模式进行了硬编码,只是为了看看它会是什么样子;但是现在我希望能够通过 UISwitch 启用和禁用它,但我不确定该怎么做?

class DarkModeTableViewCell: UITableViewCell {

var DarkisOn = Bool()
let userDefaults = UserDefaults.standard


@IBOutlet var darkModeSwitchOutlet: UISwitch!

override func awakeFromNib() {
    super.awakeFromNib()


}

override func setSelected(_ selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)

    // Configure the view for the selected state
}


@IBAction func darkModeSwitched(_ sender: Any) {

    if darkModeSwitchOutlet.isOn == true {

        //enable dark mode

        DarkisOn = true

        userDefaults.set(true, forKey: "DarkDefault")
        userDefaults.set(false, forKey: "LightDefault")



    } else {

        //enable light mode
        DarkisOn = false

        userDefaults.set(false, forKey: "DarkDefault")
        userDefaults.set(true, forKey: "LightDefault")
    }

}



}



class DarkModeViewController: UIViewController {



func set(for viewController: UIViewController) {



    viewController.view.backgroundColor = UIColor(red: 0.1, green: 0.1, blue: 0.1, alpha: 1.0)
        viewController.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
    viewController.navigationController?.navigationBar.tintColor =     UIColor.white
    viewController.navigationController?.navigationBar.barStyle =     UIBarStyle.black
    viewController.tabBarController?.tabBar.barStyle = UIBarStyle.black






}
static let instance = DarkModeViewController()
}

然后我所做的是调用每个视图控制器中的函数以查看它的外观,但是如果开关打开或关闭,我需要能够访问 bool 值然后让它执行该功能,否则只是保持不变。如果您还有其他问题,请告诉我,我知道其中一些可能没有多大意义。

更新:这个问题(因此,这个答案)是在 iOS 13 公布之前写的,因此它不使用 iOS 13 个特定的 API。


我会使用通知(NSNotificationCenter API)来解决这个问题。

这个想法是在暗模式启用和禁用时实时通知您的视图控制器,因此它们也可以实时适应变化。您不需要检查开关的状态或类似的东西。

首先创建两个通知(您也可以只创建一个并在 userInfo 字典中传入所需的主题,但在这种情况下创建两个通知更容易,因为您需要投射Swift).

NotificationsName+Extensions.swift:

import Foundation

extension Notification.Name {
    static let darkModeEnabled = Notification.Name("com.yourApp.notifications.darkModeEnabled")
    static let darkModeDisabled = Notification.Name("com.yourApp.notifications.darkModeDisabled")
}

在所有 "themable" 视图控制器上,收听这些通知:

    override func viewDidLoad() {
        super.viewDidLoad()

        // Add Observers
        NotificationCenter.default.addObserver(self, selector: #selector(darkModeEnabled(_:)), name: .darkModeEnabled, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(darkModeDisabled(_:)), name: .darkModeDisabled, object: nil)
    }

不要忘记在 deinit 中删除它们,因为向无效对象发送通知会引发异常:

deinit {
    NotificationCenter.default.removeObserver(self, name: .darkModeEnabled, object: nil)
    NotificationCenter.default.removeObserver(self, name: .darkModeDisabled, object: nil)
}

在您的 "themable" 视图控制器中,实现 darkModeEnabled(_:)darkModeDisabled(_:):

@objc private func darkModeEnabled(_ notification: Notification) {
    // Write your dark mode code here
}

@objc private func darkModeDisabled(_ notification: Notification) {
    // Write your non-dark mode code here
}

最后,切换您的开关将触发任一通知:

@IBAction func darkModeSwitched(_ sender: Any) {

    if darkModeSwitchOutlet.isOn == true {
        userDefaults.set(true, forKey: "darkModeEnabled")

        // Post the notification to let all current view controllers that the app has changed to dark mode, and they should theme themselves to reflect this change.
        NotificationCenter.default.post(name: .darkModeEnabled, object: nil)

    } else {

        userDefaults.set(false, forKey: "darkModeEnabled")

        // Post the notification to let all current view controllers that the app has changed to non-dark mode, and they should theme themselves to reflect this change.
        NotificationCenter.default.post(name: .darkModeDisabled, object: nil)
    }

}

这样,当 "theme" 发生变化时,您的所有视图控制器都会收到实时通知,并且它们会做出相应的反应。请注意,您需要采取措施在应用程序启动时显示正确的模式,但我确信您正在这样做,因为您正在使用 UserDefaults 并且可能会检查它们。另外值得一提的是 NSNotificationCenter 不是线程安全的,尽管这无关紧要,因为所有 UI 代码无论如何都应该放在主线程中。

更多信息,您可以查看NSNotificationCenter documentation

注意:这段代码是建立在 OP 的基础上的。它可以简化(例如,您不需要同时跟踪 "light" 和 "dark" 状态,只需跟踪一个)。

基本上有两种方法可以为您的应用设置主题。第一种方式:使用 Apple 的 UIAppearance proxy. This works very well if your app is very consistent about color usuage across all your views and controls, and not so well if you have a bunch of exceptions. In that case I recommend using a third party pod like SwiftTheme

请注意,这种方法已被 Apple 在全球范围内将 "dark mode" 引入(几乎)所有平台所取代。现在要走的路是 "named colors" 外观变体。

来自 iOS 13 apple 推出了深色主题,如果你想在你的 iOS 应用程序中添加深色主题,你可以在 viewDidLoad() 上应用以下代码行,例如:

        if #available(iOS 13.0, *) {
            overrideUserInterfaceStyle = .dark
        } else {
            // Fallback on earlier versions
        }

因此您可以更改主题,例如有 2 个选项浅色或深色主题。但是,如果您正在编写上面提到的代码,它将始终只在 iOS 13 运行 台设备上采用深色主题。

overrideUserInterfaceStyle = .light

或者,如果您的设备已经 运行 iOS 13,您可以更改主题,例如:

您甚至可以查看当前设置的主题,例如:

if self.traitCollection.userInterfaceStyle == .dark{
    print("Dark theme")
 }else{
    print("Light theme")
}

来看例子:

override func viewDidLoad() {
       super.viewDidLoad()

       if self.traitCollection.userInterfaceStyle == .dark{
           self.view.backgroundColor = UIColor.black
       }else{
            self.view.backgroundColor = UIColor.white
  }

}

结果:

这是相同的视频: https://youtu.be/_k6YHMFCpas

要在 iOS 13 及更高版本中支持深色模式,您可以使用单色闭包。

    @objc open class DynamicColor : NSObject{
        public   var light :  UIColor
        public   var dark :  UIColor
        public  init(light : UIColor,dark : UIColor) {
            self.light = light
            self.dark = dark
        }
    }
    extension DynamicColor{
      public  func resolve() -> UIColor{
           return UIColor.DynamicResolved(color: self)

        }
    }
    extension UIColor{
       class func DynamicResolved(color: DynamicColor) -> UIColor{
        if #available(iOS 13.0, *) {
            let dynamicColor = UIColor { (traitCollection: UITraitCollection) -> UIColor in
                if traitCollection.userInterfaceStyle == .dark {
                    return color.dark
                } else {
                    return color.light
                }
            }
            return dynamicColor
        } else {
            // Fallback on earlier versions
            return color.light
        }
        }

    }

在视图中使用它时

UIView().backgroundColor =  DynamicColor(light: .white, dark: .black).resolve()
//OR
UIView().backgroundColor =  UIColor.DynamicColor(light: .white, dark: .black)

在 SwiftUI 中,这变得更加容易。只需在视图中包含环境变量 colorScheme 并检查它的暗模式,如下所示:

struct DarkModeView: View {
    @Environment(\.colorScheme) var colorScheme: ColorScheme

    var body: some View {
        Text("Hi")
            .foregroundColor(colorScheme == .dark ? .white : .black)
    }
}

关于这一切如何运作的一篇很棒的文章here