今天的 WWDC 19 上发布了 iOS 13,我们来看下如何适配 DarkMode
首先我们来看下效果图
如何适配 DarkMode
DarkMode 主要从两个方面来适配,一是颜色,二是图片,适配的代码不是很多,接下来让我们一起来看看具体是怎么操作的吧。
颜色适配
iOS 13 之前 UIColor
只能表示一种颜色,从 iOS 13 开始 UIColor
是一个动态的颜色,它可以在 LightMode 和 DarkMode 拥有不同的颜色。
iOS 13 下 UIColor
增加了很多动态颜色,我们来看下用系统提供的颜色能实现怎么样的效果。
// UIColor 增加的颜色
@available(iOS 13.0, *)
open class var systemBackground: UIColor { get }
@available(iOS 13.0, *)
open class var label: UIColor { get }
@available(iOS 13.0, *)
open class var placeholderText: UIColor { get }
...
view.backgroundColor = UIColor.systemBackground
label.textColor = UIColor.label
placeholderLabel.textColor = UIColor.placeholderText
复制代码
怎么样,看起来和 iOS 13 之前设置一个颜色的方法一样吧,用这种动态颜色,系统直接替我们完成了适配的工作,是不是很方便呢。
如何自己创建一个动态的 UIColor
上面我们说到系统提供了一些动态的颜色供我们使用,但是在正常开发中,系统提供的颜色肯定是不够用的,所以我们要自己创建动态颜色。
iOS 13 下 UIColor
增加了一个初始化方法,我们可以用这个初始化方法来创建动态颜色。
@available(iOS 13.0, *)
public init(dynamicProvider: @escaping (UITraitCollection) -> UIColor)
复制代码
这个方法要求传一个闭包进去,当系统从 LightMode 和 DarkMode 之间切换的时候就会触发这个回调。
这个闭包返回一个 UITraitCollection
类,我们要用这个类的 userInterfaceStyle
属性。 userInterfaceStyle
是一个枚举,声明如下
@available(iOS 12.0, *)
public enum UIUserInterfaceStyle : Int {
case unspecified
case light
case dark
}
复制代码
这个枚举会告诉我们当前是 LightMode or DarkMode
现在我们创建两个 UIColor
并赋值给 view.backgroundColor
和 label
,代码如下
let backgroundColor = UIColor { (trainCollection) -> UIColor in
if trainCollection.userInterfaceStyle == .dark {
return UIColor.black
} else {
return UIColor.white
}
}
view.backgroundColor = backgroundColor
let labelColor = UIColor { (trainCollection) -> UIColor in
if trainCollection.userInterfaceStyle == .dark {
return UIColor.white
} else {
return UIColor.black
}
}
label.textColor = labelColor
复制代码
现在,我们做完了动图中背景色和文本颜色的适配,接下来我们看看图片如何适配
图片适配
打开 Assets.xcassets
把图片拖拽进去,我们可以看到这样的页面
然后我们在右侧工具栏中点击最后一栏,点击 Appearances
选择 Any, Dark
,如图所示
我们把 DarkMode 的图片拖进去,如图所示
最后我们加上 ImageView
的代码
imageView.image = UIImage(named: "icon")
复制代码
现在我们就已经完成颜色和图片的 DarkMode 适配,是不是很简单呢 (手动滑稽)
如何获取当前模式 (Light or Dark)
我们可以看到,不管是颜色还是图片,适配都是系统完成的,我们不用关心现在是什么样的样式。
但是在某些场景下,我们可能会有根据当前样式来做一些其他适配的需求,这时我们就需要知道现在什么样式。
我们可以在 UIViewController
或 UIView
中调用 traitCollection.userInterfaceStyle
来获取当前视图的样式,代码如下
if trainCollection.userInterfaceStyle == .dark {
// Dark
} else {
// Light
}
复制代码
那么我们什么时候需要用这样的方法做适配呢,比如说当我们使用 CGColor
的时候,上面说到 UIColor
在 iOS 13 下变成了一个动态颜色,但是 CGColor
仍然只能表示单一的颜色,所以当我们使用到 CGColor
的时候,我们就可以用上面的方法做适配。
颜色
对于 CGColor
我们还有还有另一种适配方法,代码如下
let resolvedColor = labelColor.resolvedColor(with: traitCollection)
layer.borderColor = resolvedColor.cgColor
复制代码
resolvedColor
方法会根据传递进去的 traitCollection
返回对应的颜色。
图片
对于 UIImage
我们也有类似的方法,代码如下
let image = UIImage(named: "icon")
let resovledImage = image?.imageAsset?.image(with: traitCollection)
复制代码
如何监听模式变化
上面我们说了如何获取当前模式,但是我们要搭配监听方法一起使用,当 light dark 模式切换的时候,要把上面的代码再执行一遍。系统为我们提供了一个回调方法,当 light dark 切换时就会触发这个方法。
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
// 适配代码
}
}
复制代码
如何改变当前模式
我们可以看到在动图中是直接改系统的模式,从而让 App 的模式修改,但是对于某些有夜间模式功能的 App 来说,如果用户打开了夜间模式,那么即使现在系统是 light 模式,也要强制用 dark 模式。
我们可以用以下代码将当前 UIViewController
或 UIView
的模式。
overrideUserInterfaceStyle = .dark
print(traitCollection.userInterfaceStyle) // dark
复制代码
我们可以看到设置了 overrideUserInterfaceStyle
之后,traitCollection.userInterfaceStyle
就是我们设置后的模式了。
需要给每一个 Controller 和 View 都设置一遍吗
答案是不需要,我们先来看一张图。
当我们设置一个 controller 为 dark 之后,这个 controller 下的 view,都会是 dark mode, 但是后续推出的 controller 仍然是跟随系统的样式。因为苹果对 overrideUserInterfaceStyle
属性的解释是这样的。
当我们在一个普通的 controlle, view 上重写这个属性,只会影响当前的视图,不会影响前面的 controller 和后续推出的 controller。
但是当我们在 window.rootViewController
上设置 overrideUserInterfaceStyle
的时候,就会影响 rootViewController
下所有的 controller, view,包括后续推出的 controller。
我们回到刚刚的问题上,如果 App 打开夜间模式,那么很简单我们只需要设置 rootViewController
的 overrideUserInterfaceStyle
属性就好了。
其他内容
Status Bar
之前 Status Bar
有两种状态,default
和 lightContent
现在 Status Bar
有三种状态,default
, darkContent
和 lightContent
现在的 darkContent
对应之前的 default
,现在的 default
会根据情况自动选择 darkContent
和 lightContent
UIActivityIndicatorView
之前的 UIActivityIndicatorView
有三种 style
分别为 whiteLarge
, white
和 gray
,现在全部废弃。
增加两种 style
分别为 medium
和 large
,指示器颜色用 color
属性修改。
如何在模式切换时打印日志
在 Arguments
中的 Arguments Passed On Launch
里面添加下面这行命令。
-UITraitCollectionChangeLoggingEnabled YES
以上是 iOS 13 如何适配 Dark Mode 的全部内容,如有错误欢迎指出。
WWDC链接 Implementing Dark Mode on iOS