首先看看我们的效果图:
开发者主要从颜色和图片两个方面进行适配,我们不需要关心切换模式时该如何操作,这些都由系统帮我们实现
UIColor
只能表示一种颜色,而从 iOS13 开始UIColor
是一个动态的颜色,在Light Mode
和Dark Mode
可以分别设置不同的颜色。@property (class, nonatomic, readonly) UIColor *systemBrownColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);@property (class, nonatomic, readonly) UIColor *systemIndigoColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);@property (class, nonatomic, readonly) UIColor *systemGray2Color API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);@property (class, nonatomic, readonly) UIColor *systemGray3Color API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);@property (class, nonatomic, readonly) UIColor *systemGray4Color API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);@property (class, nonatomic, readonly) UIColor *systemGray5Color API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);@property (class, nonatomic, readonly) UIColor *systemGray6Color API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);@property (class, nonatomic, readonly) UIColor *labelColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);@property (class, nonatomic, readonly) UIColor *secondaryLabelColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);@property (class, nonatomic, readonly) UIColor *tertiaryLabelColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);@property (class, nonatomic, readonly) UIColor *quaternaryLabelColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);@property (class, nonatomic, readonly) UIColor *linkColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);@property (class, nonatomic, readonly) UIColor *placeholderTextColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);@property (class, nonatomic, readonly) UIColor *separatorColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);@property (class, nonatomic, readonly) UIColor *opaqueSeparatorColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);@property (class, nonatomic, readonly) UIColor *systemBackgroundColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);@property (class, nonatomic, readonly) UIColor *secondarySystemBackgroundColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);@property (class, nonatomic, readonly) UIColor *tertiarySystemBackgroundColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);@property (class, nonatomic, readonly) UIColor *systemGroupedBackgroundColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);@property (class, nonatomic, readonly) UIColor *secondarySystemGroupedBackgroundColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);@property (class, nonatomic, readonly) UIColor *tertiarySystemGroupedBackgroundColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);@property (class, nonatomic, readonly) UIColor *systemFillColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);@property (class, nonatomic, readonly) UIColor *secondarySystemFillColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);@property (class, nonatomic, readonly) UIColor *tertiarySystemFillColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);@property (class, nonatomic, readonly) UIColor *quaternarySystemFillColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
[self.view setBackgroundColor:[UIColor systemBackgroundColor]];[self.titleLabel setTextColor:[UIColor labelColor]];[self.detailLabel setTextColor:[UIColor placeholderTextColor]];
用法和iOS13之前的一样,使用系统提供的这些动态颜色,不需要其余的适配操作
在实际开发过程,系统提供的这些颜色还远远不够,因而我们需要创立更多的动态颜色
iOS13 UIColor
添加了两个初始化方法,使用以下方法可以创立动态UIColor
注:一个是类方法,一个是实例方法
+ (UIColor *)colorWithDynamicProvider:(UIColor * (^)(UITraitCollection *))dynamicProvider API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);- (UIColor *)initWithDynamicProvider:(UIColor * (^)(UITraitCollection *))dynamicProvider API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
block
进去LightMode
和DarkMode
之间相互切换时就会触发此回调block
会返回一个UITraitCollection
类userInterfaceStyle
,它是一个枚举类型,会告诉我们当前是LightMode
还是DarkMode
typedef NS_ENUM(NSInteger, UIUserInterfaceStyle) { UIUserInterfaceStyleUnspecified, UIUserInterfaceStyleLight, UIUserInterfaceStyleDark,} API_AVAILABLE(tvos(10.0)) API_AVAILABLE(ios(12.0)) API_UNAVAILABLE(watchos);
UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) { if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) { return [UIColor redColor]; } else { return [UIColor greenColor]; } }]; [self.bgView setBackgroundColor:dyColor];
接下来我们看看如何适配图片
Assets.xcassets
Image set
Appearances
,选择Any,Dark
[_logoImage setImage:[UIImage imageNamed:@"icon_logo"]];
大功告成,完成颜色和图片的Dark Mode
适配,是不是很easy呢
有时候我们需要知道当前处于什么模式,并根据不同的模式执行不同的操作
iOS13中CGColor
仍然只能表示单一的颜色
通过调用UITraitCollection.currentTraitCollection.userInterfaceStyle
获取当前模式
if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { [self.titleLabel setText:@"DarkMode"]; } else { [self.titleLabel setText:@"LightMode"]; }
有时我们需要监听系统模式的变化,并作出响应
那么我们就需要在需要监听的viewController中,重写下列函数
// 注意:参数为变化前的traitCollection- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection;// 判断两个UITraitCollection对象能否不同- (BOOL)hasDifferentColorAppearanceComparedToTraitCollection:(UITraitCollection *)traitCollection;
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { [super traitCollectionDidChange:previousTraitCollection]; // trait发生了改变 if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) { // 执行操作 } }
我们知道iOS13后,
UIColor
能够表示动态颜色,但是CGColor仍然只能表示一种颜色,那么对于CALayer等对象如何适配暗黑模式呢?当然是利用上一节提到的监听模式切换的方法啦。
resolvedColor
// 通过当前traitCollection得到对应UIColor// 将UIColor转换为CGColor- (UIColor *)resolvedColorWithTraitCollection:(UITraitCollection *)traitCollection;
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { [super traitCollectionDidChange:previousTraitCollection]; UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) { if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) { return [UIColor redColor]; } else { return [UIColor greenColor]; } }]; UIColor *resolvedColor = [dyColor resolvedColorWithTraitCollection:previousTraitCollection]; layer.backgroundColor = resolvedColor.CGColor;
performAsCurrent
// 使用当前trainCollection调用此方法- (void)performAsCurrentTraitCollection:(void (^)(void))actions;
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { [super traitCollectionDidChange:previousTraitCollection]; UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) { if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) { return [UIColor redColor]; } else { return [UIColor greenColor]; } }]; [self.traitCollection performAsCurrentTraitCollection:^{ layer.backgroundColor = dyColor.CGColor; }]; }
直接设置为一个动态UIColor的CGColor就可
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { [super traitCollectionDidChange:previousTraitCollection]; UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) { if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) { return [UIColor redColor]; } else { return [UIColor greenColor]; } }]; layer.backgroundColor = dyColor.CGColor;}
??!!! 设置layer颜色都是在traitCollectionDidChange
中,意味着假如没有发生模式切换,layer将会没有颜色,需要设置一个基本颜色
模式切换时自动打印log,就不需要我们一次又一次的执行po命令了
Product
->Scheme
->Edit Scheme
Run
->Arguments
->Arguments Passed On Launch
-UITraitCollectionChangeLoggingEnabled YES
当系统设置为
Light Mode
时,对某些App的个别页面希望一直显示Dark Mode
下的样式,这个时候就需要强行设置当前ViewController
的模式了
// 设置当前view或者viewCongtroller的模式@property(nonatomic) UIUserInterfaceStyle overrideUserInterfaceStyle;
// 设置为Dark Mode就可[self setOverrideUserInterfaceStyle:UIUserInterfaceStyleDark];
overrideUserInterfaceStyle
window.rootViewController
强行设置Dark Mode
也不会影响后续present出的ViewController的模式对于UILabel、UITextField、UITextView,在设置NSAttributedString时也要考虑适配
Dark Mode
,否则在切换模式时会与背景色融合,造成不好的体验
NSDictionary *dic = @{NSFontAttributeName:[UIFont systemFontOfSize:16]};NSAttributedString *str = [[NSAttributedString alloc] initWithString:@"富文本文案" attributes:dic];
// 增加一个NSForegroundColorAttributeName属性NSDictionary *dic = @{NSFontAttributeName:[UIFont systemFontOfSize:16],NSForegroundColorAttributeName:[UIColor labelColor]};NSAttributedString *str = [[NSAttributedString alloc] initWithString:@"富文本文案" attributes:dic];
总的来说,iOS13主要有以下变化:
1.支持 Dark Mode
2.UIColor变为动态颜色
3.升级StatusBar样式
4.升级UIActivityIndicatorView样式
完整iOS13新特性请参考以下文章:
iOS13 新特性简介
iOS13-适配夜间模式/深色外观(Dark Mode)
)