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

暗黑模式

JKSwiftExtensionJKDarkModeUtilJKThemeProvider 有源码

  • 目录:
    • 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 为 UIUserInterfaceStyle,类型为 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 给个星星,谢谢
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容