苹果在iOS 13推出了夜间模式,应用默认支持这个模式。如果应用没有适配,用户在设置-显示与亮度-外观,选择深色后,应用在运行时会出现某些未设置颜色的view变成黑色,导致界面很不美观。
苹果官方强烈推荐适配夜间模式,但是也可以通过UIUserInterfaceStyle临时性地退出夜间模式。文档地址
动态颜色
夜间模式的核心是动态颜色,动态颜色属于UIColor对象,只是它在Dark和Light两种模式下值不一样。
动态颜色可以通过以下两个方法来创建:
+ (UIColor *)colorWithDynamicProvider:(UIColor* (^)(UITraitCollection *traitCollection))dynamicProvider
- (UIColor *)initWithDynamicProvider:(UIColor* (^)(UITraitCollection *traitCollection))dynamicProvider
比如:
[UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traitCollection) {
if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
return [UIColor blackColor];//返回固定颜色,而不是动态颜色
} else {
return [UIColor whiteColor];//返回固定颜色,而不是动态颜色
}
}];
在.xcassets后缀文件中也可以创建动态颜色(新建 Color Set),如下图:
新建好Color Set后通过
[UIColor colorWithName:@"colorName"]获取到颜色对象,然后使用- (UIColor *)resolvedColorWithTraitCollection:(UITraitCollection* )traitCollection方法解析得到对应到颜色。
以上是自定义动态颜色的方法,一般是在苹果提供的系统自带动态颜色不满足使用需求时使用,这些颜色声明在UIInterface.h文件中,可以直接使用。比如:systemBackgroundColor、secondarySystemBackgroundColor和tertiarySystemBackgroundColor。
动态图片
动态图片和动态颜色一样,具体参考文档:developer.apple.com/documentati… 为了节省包大小,大部分情况下不提供两套图片,而是切换到夜间模式时在图片上加上一层蒙板,以降低图片亮度。
夜间模式切换过程
当用户切换Dark或者Light外观时,系统会通知每个window,调用渲染相关的方法,重新渲染UI。具体的方法如下:
View层:
traitCollectionDidChange(_:)
layoutSubviews()
draw(_:)
updateConstraints()
tintColorDidChange()
UIViewController层:
traitCollectionDidChange(_:)
updateViewConstraints()
viewWillLayoutSubviews()
viewDidLayoutSubviews()
UIPresentationController层:
traitCollectionDidChange(_:)
containerViewWillLayoutSubviews()
containerViewDidLayoutSubviews()
在调用这些方法之前,每个视图的traitCollection会得到更新,所以可以在这些方法中使用resolvedColorWithTraitCollection方法解析出目标颜色并应用到View上,以实现夜间/白天模式的切换。
traitCollection是外观特征的集合,其中就包括当前是Dark外观还是Light外观的信息。
以上方法中,traitCollectionDidChange的使用方式如下:
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
[super traitCollectionDidChange:previousTraitCollection];
if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) {
self.titleLabel.textColor = [[UIColor secondaryLabelColor] resolvedColorWithTraitCollection:self.traitCollection];
}
}
调试
debug模式下要打印traitCollection相关信息,设置UITraitCollectionChangeLoggingEnabled为YES