Swift:暗黑模式iOS 13以上支持是否跟随系统和iOS13
JKSwiftExtension 中JKDarkModeUtil
和 JKThemeProvider
有源码
- 目录:
- 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关闭暗黑模式
在Info.plist
文件中,添加 key 为User Interface Style
,类型为 String,value 设置为Light
即可
-
方式二:使用代码的方式
强制关闭暗黑模式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、提示:在使用
JKDarkModeUtil
和JKThemeProvider
的时候,需要在设置window
的rootViewController
后需要调用JKDarkModeUtil.defaultDark()