iOS Dark Mode
参考文档:
如何适配深色模式呢?参考官方文档Supporting Dark Mode in Your Interface
1.颜色
为UI选择自适应颜色,Light和Dark界面选择不同的颜色调色板,有2中方式
- 选择语义颜色(semantic colors)而不是固定颜色值,如使用
labelColor
- 在assets目录中定义自定义颜色
参考UI Element Colors,有如下的分类
- Label Colors - 标签颜色
- label
- secondaryLabel
- tertiaryLabel
- quaternaryLabel
- Standard Content Background Colors - 标准内容背景颜色
- systemBackground - 界面主要背景的颜色
- secondarySystemBackground
- tertiarySystemBackground
以Dark Mode: The Basics中的例子来说,在Light模式时,显示效果如下:
而在Dark模式下,UITabBar会自动变成Dark模式,但是UILabel就可不见了(颜色问题)
现在来修改
1.将controller的背景颜色修改为system background color,在dark模式下,显示效果如下,label可见了:
2.Dark模式下使用不同的图片
在Appearances下选择Any, Dark
,社会Dark模式下的图片
3.通过代码设置secondaryView
的背景颜色
self.secondaryView.backgroundColor = .secondarySystemBackground
但如果想在Light模式下,背景颜色为blue,Dark模式下,背景颜色为yellow,该如何实现呢?此时要使用动态颜色
public init(dynamicProvider: @escaping (UITraitCollection) -> UIColor)
如下:
let dynamicColor = UIColor.init { (traitCollection) -> UIColor in
if traitCollection.userInterfaceStyle == .dark {
return .yellow
} else {
return .blue
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.secondaryView.backgroundColor = dynamicColor
}
如何显示Dark Mode
在Interface Builder
中通过如下的选项来切换
对于模拟器,通过如下的设置来切换
UITraitCollection
可以使用 UITraitCollection.current
这个类属性来获取当前 App 的颜色模式。不过需要注意的是在,并不是在所有的地方使用这个 API 都是正确的,只有在下面这些方法中,才可以放心的使用这个 API:
UIView | UIViewController | UIPresentationController |
---|---|---|
draw() | ||
layoutSubview() | viewWillLayoutSubviews() viewDidLayoutSubviews() | containerViewWillLayoutSubviews() containerViewDidLayoutSubviews() |
traitCollectionDidChange() tintColorDidChange() | traitCollectionDidChange() | traitCollectionDidChange() |
看下定义:
extension UITraitCollection {
/* The current trait collection, used when resolving the appearance of dynamic UIColors and similar objects.
* This is a thread-local property, so it may be changed on non-main threads without affecting the main thread.
*/
@available(iOS 13.0, *)
open class var current: UITraitCollection
/* Sets `UITraitCollection.currentTraitCollection` to this trait collection, performs the given actions,
* then restores `UITraitCollection.currentTraitCollection` to its original value.
* Just like `currentTraitCollection`, this only affects the current thread, and may be used on non-main threads
* without affecting the main thread.
*/
@available(iOS 13.0, *)
open func performAsCurrent(_ actions: () -> Void)
}
可参考:
颜色
CALayer
和CGColor
并不了解动态颜色,因此,假设有CALayer
,并且需要设置其边框颜色为不能动态显示的cgColor
。 那么如何解决这种颜色?
let layer = Layer()
let traitCollection = view.traitCollection
方法一:
let resolvedColor = UIColor.label.resolvedColor(with: traitCollection)
layer.borderColor = resolvedColor.cgColor
方法二:
将traitCollection
作为当前的trait collection
方法performAsCurrent
的解释如下:
将
UITraitCollection.currentTraitCollection
设置为这个trait collection。并执行给定的操作,然后将UITraitCollection.currentTraitCollection
恢复为原始值
traitCollection.performAsCurrent { layer.borderColor = UIColor.label.cgColor }
方法三:
let savedTraitCollection = UITraitCollection.current
UITraitCollection.current = traitCollection
layer.borderColor = UIColor.label.cgColor
UITraitCollection.current = savedTraitCollection
图片
guard let image = UIImage(named: named) else { return }
let assets = image.imageAsset
guard let resolvedImage = assets?.image(with: self.traitCollection) else { return }
self.imageView.image = resolvedImage
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
//手动处理颜色
let layer = CALayer()
let traitCollection = view.traitCollection
//1 - ask color to resolve itself
let resolvedColor = UIColor.label.resolvedColor(with: traitCollection)
layer.borderColor = resolvedColor.cgColor
//2
traitCollection.performAsCurrent {
layer.borderColor = UIColor.label.cgColor
}
//3
let savedTraitCollection = UITraitCollection.current
UITraitCollection.current = traitCollection
layer.borderColor = UIColor.label.cgColor
UITraitCollection.current = savedTraitCollection
//图片
let image = UIImage(named: "sampleIMG")
let asset = image?.imageAsset
let resolvedImg = asset?.image(with: traitCollection)
}
}