Swift:暗黑模式iOS 13以上支持是否跟随系统和iOS13以下的主题适配

暗黑模式

JKSwiftExtension 中JKDarkModeUtilJKThemeProvider 有源码

  • 目录:
    • 1、暗黑模式介绍
    • 2、暗黑模式的关闭和打开
    • 3、iOS 13 暗黑模式的适配 (重点看)
    • 4、iOS 13 以下主题的适配:浅色和深色 (重点看)
    • 5、一次适配不完的问题
一、暗黑模式的介绍
  • 1.1、暗黑模式的简介
    从 iOS 13.0 版本开始,用户可以选择采用系统范围内的浅色或深色外观。 深色外观(称为暗黑模式DarkMode)实现了许多应用程序已经采用的界面样式。 用户可以选择自己喜欢的美学,也可以选择根据环境照明条件或特定时间表来切换其界面。


    所有应用程序都应该支持浅色和深色界面样式,但在某些地方的特定外观可能会表现更好。例如,您可能总是为印刷内容采用轻巧的外观。
    在更改代码之前,请打开黑暗模式,看看应用程序的反应。系统为您做了很多工作,如果您的应用程序使用标准视图和控件,您可能不需要进行许多更改。标准视图和控件会自动更新其外观,以匹配当前界面样式。如果您已经使用颜色和图像资产,您可以在不更改代码的情况下添加深色变体。

  • 1.2、UITraitCollection介绍
    在 iOS 13 中,我们可以通过 UITraitCollection 来判断当前系统的模式。UIView 和 UIViewController 、UIScreen、UIWindow 都已经遵从了UITraitEnvironment 这个协议,因此这些类都拥有一个叫做 traitCollection 的属性,在这些类中,我们可以这样去判断当前 App 的颜色模式:

    let isDark = self.traitCollection.userInterfaceStyle == .dark
    
  • 1.3、模拟器暗黑模式切换的快捷键:command+shift+A

二、暗黑模式的关闭和打开

  • 2.1、自己的app还没有适配暗黑模式又担心用户使用暗黑模式的时候界面不好看:关闭暗黑模式

    • 方式一:暂时全局关闭暗黑模式:
      Info.plist 文件中,添加 key 为 User Interface Style,类型为 String,value 设置为 Light 即可

      Info.plist关闭暗黑模式

    • 方式二:使用代码的方式
      强制关闭暗黑模式

      if(@available(iOS 13.0,*)){
         UIApplication.shared.windows.filter({$0.isKeyWindow}).first?.overrideUserInterfaceStyle = .light
      }
      
  • 2.2、单个页面关闭暗黑模式

    self.overrideUserInterfaceStyle = .light
    

    提示:设置此属性会影响当前 view/viewController/window 以及它下面的任何内容

    如果你希望一个子视图监听系统的模式,请将 overrideUserInterfaceStyle 属性设置为unspecified

    self.overrideUserInterfaceStyle = .unspecified
    
三、iOS 13 暗黑模式的适配
  • 3.1、iOS 13的暗黑模式我们主要分为:跟随系统模式自定义模式

    • 跟随系统,需要设置:

      UIApplication.shared.windows.filter({$0.isKeyWindow}).first?.overrideUserInterfaceStyle = .unspecified
      
    • 不跟随系统
      浅色:UIApplication.shared.windows.filter({$0.isKeyWindow}).first?.overrideUserInterfaceStyle = .light
      深色:UIApplication.shared.windows.filter({$0.isKeyWindow}).first?.overrideUserInterfaceStyle = .dark

  • 3.2、iOS 13的暗黑模式适配代码在:JKDarkModeUtil 里面,默认是 跟随系统模式,主要代码如下

    import UIKit
    
    public class JKDarkModeUtil {
      
        /// 跟随系统的key
        private static let JKDarkToSystem = "JKDarkToSystem"
        /// 是否浅色模式的key
        private static let JKLightDark = "JKLightDark"
        /// 是否浅色
        public static var isLight: Bool {
            if let value = UserDefaults.jk.userDefaultsGetValue(key: JKLightDark) as? Bool {
                return value
            }
            return true
        }
        /// 是否跟随系统
        public static var isFloorSystem: Bool {
            if #available(iOS 13, *) {
                if let value = UserDefaults.jk.userDefaultsGetValue(key: JKDarkToSystem) as? Bool {
                    return value
                }
                return true
            }
            return false
        }
    }
    
    // MARK:- 方法的调用
    extension JKDarkModeUtil: JKThemeable {
        public func apply() {}
    }
    
    public extension JKDarkModeUtil {
      
        // MARK: 初始化的调用
        /// 默认设置
        static func defaultDark() {
            if #available(iOS 13.0, *) {
                // 默认跟随系统暗黑模式开启监听
                if (JKDarkModeUtil.isFloorSystem) {
                    JKDarkModeUtil.setDarkModeFollowSystem(isFollowSystem: true)
                } else {
                    UIApplication.shared.windows.filter({$0.isKeyWindow}).first?.overrideUserInterfaceStyle = JKDarkModeUtil.isLight ? .light : .dark
                }
            }
        }
      
        // MARK: 设置系统是否跟随
        static func setDarkModeFollowSystem(isFollowSystem: Bool) {
            if #available(iOS 13.0, *) {
                // 1.1、设置是否跟随系统
                UserDefaults.jk.userDefaultsSetValue(value: isFollowSystem, key: JKDarkToSystem)
                let result = UITraitCollection.current.userInterfaceStyle == .light ? true : false
                UserDefaults.jk.userDefaultsSetValue(value: result, key: JKLightDark)
                // 1.2、设置模式的保存
                if isFollowSystem {
                    UIApplication.shared.windows.filter({$0.isKeyWindow}).first?.overrideUserInterfaceStyle = .unspecified
                } else {
                    UIApplication.shared.windows.filter({$0.isKeyWindow}).first?.overrideUserInterfaceStyle = UITraitCollection.current.userInterfaceStyle
                }
            }
        }
      
        // MARK: 设置:浅色 / 深色
        static func setDarkModeCustom(isLight: Bool) {
            if #available(iOS 13.0, *) {
                // 1.1、只要设置了模式:就是黑或者白
                UIApplication.shared.windows.filter({$0.isKeyWindow}).first?.overrideUserInterfaceStyle = isLight ? .light : .dark
                // 1.2、设置跟随系统:否
                UserDefaults.jk.userDefaultsSetValue(value: false, key: JKDarkToSystem)
                UserDefaults.jk.userDefaultsSetValue(value: isLight, key: JKLightDark)
            } else {
                // 模式存储
                UserDefaults.jk.userDefaultsSetValue(value: isLight, key: JKLightDark)
                // 通知模式更新
                LegacyThemeProvider.shared.updateTheme()
            }
        }
    }
    
    // MARK:- 动态颜色的使用
    public extension JKDarkModeUtil {
        static func colorLightDark(light: UIColor, dark: UIColor) -> UIColor {
            if #available(iOS 13.0, *) {
                return UIColor { (traitCollection) -> UIColor in
                    if JKDarkModeUtil.isFloorSystem {
                        if traitCollection.userInterfaceStyle == .light {
                            return light
                        } else {
                            return dark
                        }
                    } else {
                        return JKDarkModeUtil.isLight ? light : dark
                    }
                }
            } else {
                // iOS 13 以下主题色的使用
                if JKDarkModeUtil.isLight {
                    return light
                }
                return dark
            }
        }
    }
    
    // MARK:- 动态图片的使用
    public extension JKDarkModeUtil {
    
        // MARK: 深色图片和浅色图片切换 (深色模式适配)
        /// 深色图片和浅色图片切换 (深色模式适配)
        /// - Parameters:
        ///   - light: 浅色图片
        ///   - dark: 深色图片
        /// - Returns: 最终图片
        static func image(light: UIImage?, dark: UIImage?) -> UIImage? {
            if #available(iOS 13.0, *) {
                guard let weakLight = light, let weakDark = dark, let config = weakLight.configuration else { return light }
                let lightImage = weakLight.withConfiguration(config.withTraitCollection(UITraitCollection.init(userInterfaceStyle: UIUserInterfaceStyle.light)))
                lightImage.imageAsset?.register(weakDark, with: config.withTraitCollection(UITraitCollection(userInterfaceStyle: UIUserInterfaceStyle.dark)))
                return lightImage.imageAsset?.image(with: UITraitCollection.current) ?? light
            } else {
                // iOS 13 以下主题色的使用
                if JKDarkModeUtil.isLight {
                    return light
                }
                return dark
            }
        }
    }
    
  • 3.3、UIColor 颜色的使用方式

    JKDarkModeUtil.colorLightDark(light: UIColor.yellow, dark: UIColor.green)
    

    提示:建议自定义业务色,如下

    // MARK:- 业务颜色的使用
    extension UIColor {
        /// 背景色
        private(set) static var cA1 = JKDarkModeUtil.colorLightDark(light: UIColor.yellow, dark: UIColor.green)
    }
    

    使用:self.view.backgroundColor = .cA1

  • 3.4、CGColor 适配,实现 traitCollectionDidChange 方法,适配颜色即可

    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        super.traitCollectionDidChange(previousTraitCollection)
        // cgColor 颜色适配
        button.layer.borderColor = JKDarkModeUtil.colorLightDark(light: .green, dark: .brown).cgColor
    }
    

    提示: 完整的触发方法文档

  • 3.5、暗黑模式图片的使用(选择:Any,Dark)

    • Assets中添加图片


      暗黑模式图片的使用
    • 代码适配图片

      // MARK:- 动态图片的使用(网络图片一样)
      public extension JKDarkModeUtil {
      
          // MARK: 深色图片和浅色图片切换 (深色模式适配)
          /// 深色图片和浅色图片切换 (深色模式适配)
          /// - Parameters:
          ///   - light: 浅色图片
          ///   - dark: 深色图片
          /// - Returns: 最终图片
          static func image(light: UIImage?, dark: UIImage?) -> UIImage? {
               if #available(iOS 13.0, *) {
                  guard let weakLight = light, let weakDark = dark, let config = weakLight.configuration else { return light }
                  let lightImage = weakLight.withConfiguration(config.withTraitCollection(UITraitCollection.init(userInterfaceStyle: UIUserInterfaceStyle.light)))
                  lightImage.imageAsset?.register(weakDark, with: config.withTraitCollection(UITraitCollection(userInterfaceStyle: UIUserInterfaceStyle.dark)))
                  return lightImage.imageAsset?.image(with: UITraitCollection.current) ?? light
               } else {
                  // iOS 13 以下主题色的使用
                  if JKDarkModeUtil.isLight {
                     return light
                  }
                  return dark
               }
         }
      }
      
      • 提示:代码适配的时候需要在:traitCollectionDidChange 方法里面再次赋值,iOS 13 以上的建议直接再 Assets中添加图片
        如果是 cell 里面的图直接使用方法加载图片,刷新一下视图即可
四、iOS 13 以下主题的适配:浅色和深色
  • 4.1、iOS 13 以下我们采用的是监听模式来改变主题色,源码在 JKThemeProvider,使用步骤如下

    • 1>、遵守协议 JKThemeable (遵守UITraitEnvironment协议的均可使用)

      extension ViewController: JKThemeable {
          func apply() {
             self.view.backgroundColor = JKDarkModeUtil.colorLightDark(light: UIColor.yellow, dark: UIColor.green)
          }
      }
      
    • 2>、注册监听模式变化

       themeProvider.register(observer: self)
      
    • 3>、模式变化通知

      JKDarkModeUtil.setDarkModeCustom(isLight: true) 
      
  • 4.2、颜色的调用和图片模式的调用和iOS13一样

    • 颜色调用

      JKDarkModeUtil.colorLightDark(light: UIColor.yellow, dark: UIColor.green)
      
    • 图片调用

      JKDarkModeUtil.image(light: UIImage(named: "tabbar_profile"), dark: UIImage(named: "tabbar_profile_selected"))
      
  • 4.3、主题变化后会走代理 apply(),在代理方法里面我们只需要重新调用颜色或者图片的方法即可

    extension ViewController: JKThemeable {
        func apply() {
            self.view.backgroundColor = JKDarkModeUtil.colorLightDark(light: UIColor.yellow, dark: UIColor.green)
            darkImageView.image = JKDarkModeUtil.image(light: UIImage(named: "tabbar_profile"), dark: UIImage(named: "tabbar_profile_selected"))
        }
    }
    
五、一次适配不完的问题
  • 5.1、一次适配不完的问题,我们在使用JKDarkModeUtil 调用一些方法的时候,适配哪里就哪里使用即可,不使用的就按照之前的颜色以及图片调用即可
  • 5.2、提示:在使用 JKDarkModeUtilJKThemeProvider 的时候,需要在设置 windowrootViewController 后需要调用 JKDarkModeUtil.defaultDark()
如果能帮到您,请在 JKSwiftExtension 给个星星,谢谢

你可能感兴趣的:(Swift:暗黑模式iOS 13以上支持是否跟随系统和iOS13以下的主题适配)