将暗黑模式添加到iOS应用程序

16

我正在尝试为我的应用添加一个主题(深色主题)。因此,当用户点击活动开关时,整个应用程序将进入暗模式。我已经硬编码了暗模式,只是为了看看效果; 现在,我想通过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()
}

然后我所做的就是在每个视图控制器中调用该函数以查看它的外观,但我需要能够访问开关是否打开或关闭的布尔值,如果是,则执行该功能,否则保持不变。如果您有任何进一步的问题,请让我知道,我知道这些可能没有太多意义。

6个回答

18

更新:本问题(以及因此,本答案)是在iOS 13宣布之前编写的,因此不使用iOS 13特定的API。


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

思路是实时通知您的视图控制器何时启用暗模式以及何时禁用暗模式,因此它们也可以实时适应更改。您不需要检查开关的状态或任何其他东西。

首先创建两个通知(您也可以仅使用一个并在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")
}

在所有的“可主题化”的视图控制器上,监听以下通知:

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

在您的“可主题化”的视图控制器中,实现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)
    }

}

通过这种方式,当 "主题" 更改时,所有视图控制器将实时收到通知并相应地做出反应。请注意,您需要采取措施在应用程序启动时显示正确的模式,但我相信您会这样做,因为您正在使用UserDefaults并且可能正在检查它们。值得一提的是NSNotificationCenter不是线程安全的,尽管这些都是应该放在主线程中的UI代码,所以这不应该成为问题。

有关更多信息,可以查阅NSNotificationCenter文档

注意:此代码基于原帖。它可以简化(例如,您不需要跟踪"light"和"dark"两种状态,只需要一个即可)。


1
这是一个“特殊”的函数,不需要“func”关键字。因此,在类中与所有其他方法具有相同的作用域。 - Andy Ibanez
想知道是否定义一个协议来处理暗模式方法会不错。 - Woodstock
@Andy,确实是这样,但我不想在视图加载后再次应用夜间模式并使这些更改产生动画效果,你知道我的意思吗? - Woodstock
2
@Woodstock viewDidLoad 在视图实际可见之前被调用。在向用户显示视图之前,将应用暗模式。 - Andy Ibanez
1
那些仅仅是与代码相关的细节,你不需要它们来实现暗黑模式切换功能。只需理解通知系统和UserDefaults即可。 - Andy Ibanez
显示剩余30条评论

6

iOS 13 开始,苹果推出了暗黑主题。如果您想在 iOS 应用中加入暗黑主题,可以在 viewDidLoad() 中应用以下代码:

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

你可以像这样更改主题,有两个选项:浅色主题和深色主题。但是如果您编写了上述代码,则在运行iOS 13的设备上始终只会使用深色主题。

overrideUserInterfaceStyle = .light

如果您的设备已经运行在iOS 13系统上,您可以像下面这样更改主题:

enter image description here

您甚至可以检查当前设置的主题:

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


5

基本上有两种方法来为您的应用程序设置主题。第一种方法:使用苹果的UIAppearance代理。如果您的应用程序在所有视图和控件中非常一致地使用颜色,则此方法非常有效,但如果有许多异常情况,则效果不佳。在这种情况下,我建议使用第三方pod,例如SwiftTheme


谢谢。我会在电脑前查看这个问题。我的应用程序颜色保持一致。除了用户个人资料图片等,所有东西基本上都是白色和黑色。 - Jaqueline
我认为,这是完美的答案。 - Hardik Shah
或者选择第三种选项,以最简单和最安全的方式自己完成通知。第三方库应该始终是最后的选择。 - trndjc

4
请注意,这种方法已被全球范围内的苹果公司引入“深色模式”所取代,适用于(几乎)所有平台。现在应该采用的是具有外观变体的“命名颜色”。

3

为了在iOS 13及以上版本中支持暗黑模式,你可以使用unicolor闭包。

    @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)

0
在SwiftUI中,这变得更加容易。只需在视图中包含环境变量colorScheme并检查它是否为暗模式即可:
struct DarkModeView: View {
    @Environment(\.colorScheme) var colorScheme: ColorScheme

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

这里有一篇很棒的文章,讲述了所有这些是如何工作的here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接