【问题标题】:Adding dark mode to iOS app将暗模式添加到 iOS 应用程序
【发布时间】:2018-05-10 02:30:52
【问题描述】:

我正在尝试向我的应用程序添加一个主题(深色主题)。因此,当用户单击活动开关时,它将使整个应用程序进入黑暗模式。我对暗模式进行了硬编码,只是为了看看它会是什么样子;但是现在我希望能够通过 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 swift themes uiswitch


    【解决方案1】:

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


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

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

    首先创建两个通知(您也可以只使用一个并在 userInfo 字典中传递所需的主题,但在这种情况下创建两个通知更容易,因为您需要投射和其他与斯威夫特)。

    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 documentation

    注意:此代码建立在 OP 的基础上。它可以被简化(例如,您不需要同时跟踪“亮”和“暗”状态,只需一个)。

    【讨论】:

    • 非常感谢您!看起来它会完美运行!以后有机会我会试试这个,如果我有任何问题,我会回复你。
    • @Jaqueline 没问题!如果对您有帮助,请不要忘记将答案标记为已接受。谢谢!
    • 这是一个不需要“func”关键字的“特殊”函数。因此,与类中所有其他方法的范围相同。
    • @Woodstock viewDidLoad 在您的视图实际可见之前被调用。将在向用户显示视图之前应用暗模式。
    • 这只是特定于操作代码的东西。您不需要它来实现暗模式更改功能。只需了解通知系统和 UserDefaults 即可。
    【解决方案2】:

    从 iOS 13 苹果推出深色主题,如果您想在 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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 谢谢。当我在我的电脑上时,我会研究这个。我的应用程序与颜色保持一致。除了用户个人资料图片等以外,一切都以白色和黑色为主。
      • 我认为,这是完美的答案
      • 或选项 3,只需使用最简单、最安全的方式通知您自己即可。第三方库应该是最后的手段。
      【解决方案4】:

      请注意,这种方法已被 Apple 在全球范围内(几乎)所有平台引入“黑暗模式”所取代。现在要走的路是带有外观变体的“命名颜色”。

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          【解决方案6】:

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

          struct DarkModeView: View {
              @Environment(\.colorScheme) var colorScheme: ColorScheme
          
              var body: some View {
                  Text("Hi")
                      .foregroundColor(colorScheme == .dark ? .white : .black)
              }
          }
          

          有一篇很棒的文章介绍了这一切是如何工作的here

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-09-30
            • 1970-01-01
            • 2013-02-16
            • 1970-01-01
            • 2020-08-16
            • 2020-05-15
            • 2019-04-02
            • 2011-12-04
            相关资源
            最近更新 更多