目录
1.适配暗黑模式(Dark Mode)
1.1颜色适配
* 系统动态颜色*
* 自定义动态UIColor(代码自定义、xib自定义)*
2.获取当前模式(Light or Dark)
3. 不同模式的监听
4. CGcolor适配
5. 图片适配
6. 其他
1.适配暗黑模式(Dark Mode)
1.1颜色适配
iOS13 之前 UIColor只能表示一种颜色,而从 iOS13 开始UIColor是一个动态的颜色,在Light Mode和Dark Mode可以分别设置不同的颜色。
iOS13系统提供了一些动态颜色
@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);
1.1.1 系统动态颜色使用
[self.view setBackgroundColor:[UIColor systemBackgroundColor]];
[self.titleLabel setTextColor:[UIColor labelColor]];
[self.detailLabel setTextColor:[UIColor placeholderTextColor]];
1.1.2 自定义动态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];
实际开发中颜色适配的使用
纯代码开发
iOS13之后苹果将UIColor改变成了动态颜色,UIColor会根据不同模式来改变不同的颜色。系统也提供了初始化方法。
接下来是我自己对UIColor进行的category,这里提供了一些设置颜色的方法
以下代码是category类UIColor+XY.h
//UIColor+XY.h
#import <UIKit/UIKit.h>
@interface UIColor (XY)
/// 十六进制字符串获取颜色
/// @param color 16进制色值 支持@“#123456”、 @“0X123456”、 @“123456”三种格式
+ (UIColor *)colorWithHexString:(NSString *)color;
/// 十六进制字符串获取颜色
/// @param color 16进制色值 支持@“#123456”、 @“0X123456”、 @“123456”三种格式
/// @param alpha 透明度
+ (UIColor *)colorWithHexString:(NSString *)color alpha:(CGFloat)alpha;
/// 适配暗黑模式颜色 传入的UIColor对象
/// @param lightColor 普通模式颜色
/// @param darkColor 暗黑模式颜色
+ (UIColor *)colorWithLightColor:(UIColor *)lightColor DarkColor:(UIColor *)darkColor;
/// 适配暗黑模式颜色 颜色传入的是16进制字符串
/// @param lightColor 普通模式颜色
/// @param darkColor 暗黑模式颜色
+ (UIColor *)colorWithLightColorStr:(NSString *)lightColor DarkColor:(NSString *)darkColor;
/// 适配暗黑模式颜色 颜色传入的是16进制字符串 还有颜色的透明度
/// @param lightColor 普通模式颜色
/// @param lightAlpha 普通模式颜色透明度
/// @param darkColor 暗黑模式颜色透明度
/// @param darkAlpha 暗黑模式颜色
+ (UIColor *)colorWithLightColorStr:(NSString *)lightColor WithLightColorAlpha:(CGFloat)lightAlpha DarkColor:(NSString *)darkColor WithDarkColorAlpha:(CGFloat)darkAlpha;
@end
category类UIColor+XY.m
//
// UIColor+XY.m
#import "UIColor+XY.h"
@implementation UIColor (XY)
///十六进制字符串获取颜色
/// @param color 16进制色值 支持@“#123456”、 @“0X123456”、 @“123456”三种格式
+ (UIColor *)colorWithHexString:(NSString *)color{
return [self colorWithHexString:color alpha:1.0f];
}
/// 十六进制字符串获取颜色
/// @param color 16进制色值 支持@“#123456”、 @“0X123456”、 @“123456”三种格式
/// @param alpha 透明度
+ (UIColor *)colorWithHexString:(NSString *)color alpha:(CGFloat)alpha{
//删除字符串中的空格
NSString *cString = [[color stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] uppercaseString];
// String should be 6 or 8 characters
if ([cString length] < 6){
return [UIColor clearColor];
}
// strip 0X if it appears
//如果是0x开头的,那么截取字符串,字符串从索引为2的位置开始,一直到末尾
if ([cString hasPrefix:@"0X"]){
cString = [cString substringFromIndex:2];
}
//如果是#开头的,那么截取字符串,字符串从索引为1的位置开始,一直到末尾
if ([cString hasPrefix:@"#"]){
cString = [cString substringFromIndex:1];
}
if ([cString length] != 6){
return [UIColor clearColor];
}
// Separate into r, g, b substrings
NSRange range;
range.location = 0;
range.length = 2;
//r
NSString *rString = [cString substringWithRange:range];
//g
range.location = 2;
NSString *gString = [cString substringWithRange:range];
//b
range.location = 4;
NSString *bString = [cString substringWithRange:range];
// Scan values
unsigned int r, g, b;
[[NSScanner scannerWithString:rString] scanHexInt:&r];
[[NSScanner scannerWithString:gString] scanHexInt:&g];
[[NSScanner scannerWithString:bString] scanHexInt:&b];
return [UIColor colorWithRed:((float)r / 255.0f) green:((float)g / 255.0f) blue:((float)b / 255.0f) alpha:alpha];
}
/// 适配暗黑模式颜色 传入的UIColor对象
/// @param lightColor 普通模式颜色
/// @param darkColor 暗黑模式颜色
+ (UIColor *)colorWithLightColor:(UIColor *)lightColor DarkColor:(UIColor *)darkColor {
if (@available(iOS 13.0, *)) {
return [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) {
if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {
return lightColor;
} else {
return darkColor;
}
}];
} else {
return lightColor ? lightColor : (darkColor ? darkColor : [UIColor clearColor]);
}
}
/// 适配暗黑模式颜色 颜色传入的是16进制字符串
/// @param lightColor 普通模式颜色
/// @param darkColor 暗黑模式颜色
+ (UIColor *)colorWithLightColorStr:(NSString *)lightColor DarkColor:(NSString *)darkColor{
return [UIColor colorWithLightColor:[UIColor colorWithHexString:lightColor] DarkColor:[UIColor colorWithHexString:darkColor]];
}
/// 适配暗黑模式颜色 颜色传入的是16进制字符串 还有颜色的透明度
/// @param lightColor 普通模式颜色
/// @param lightAlpha 普通模式颜色透明度
/// @param darkColor 暗黑模式颜色透明度
/// @param darkAlpha 暗黑模式颜色
+ (UIColor *)colorWithLightColorStr:(NSString *)lightColor WithLightColorAlpha:(CGFloat)lightAlpha DarkColor:(NSString *)darkColor WithDarkColorAlpha:(CGFloat)darkAlpha{
return [UIColor colorWithLightColor:[UIColor colorWithHexString:lightColor alpha:lightAlpha] DarkColor:[UIColor colorWithHexString:darkColor alpha:darkAlpha]];
}
@end
在pch文件添加宏定义
///适配暗黑模式 lightColor:白天模式颜色 darkColor:暗黑模式颜色
#define KCustomAdjustColor(lightColor, darkColor) [UIColor colorWithLightColor:lightColor DarkColor:darkColor]
使用
//只需在给颜色赋值的时候指定不同模式下的颜色即可
self.customColorLabel.backgroundColor = [UIColor colorWithLightColorStr:@"226597" DarkColor:@"5ACFB1"] ;
xib开发
对于使用xib的童鞋来说,我们就只能使用color set颜色集了。
*新建Color Set *
*使用方法: *
///用ColorSet 跟UIImage使用是类似的 把自定义的颜色名字写入就行了
self.colorSetLabel.textColor = [UIColor colorNamed:@"customBlueColor"];
要进行CGColor的适配,需要先了解监听模式的切换
2. 获取当前模式(Light or Dark)
iOS13中CGColor
依然只能表示单一的颜色
通过调用UITraitCollection.currentTraitCollection.userInterfaceStyle
获取当前模式
if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
[self.titleLabel setText:@"DarkMode"];
}
else {
[self.titleLabel setText:@"LightMode"];
}
3. 监听模式的切换
有时我们需要监听系统模式的变化,并作出响应
那么我们就需要在需要监听的viewController
中,重写下列函数
// 注意:参数为变化前的traitCollection
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection;
// 判断两个UITraitCollection对象是否不同
- (BOOL)hasDifferentColorAppearanceComparedToTraitCollection:(UITraitCollection *)traitCollection;
使用
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
[super traitCollectionDidChange:previousTraitCollection];
// trait发生了改变
if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) {
// 执行操作
}
}
⚠️⚠️⚠️⚠️⚠️⚠️⚠️特别注意:
当用户更改外观时,系统会通知所有window与View需要更新样式,在此过程中iOS会触发以下方法完整的触发方法文档
UIView
traitCollectionDidChange(_:)
layoutSubviews()
draw(_:)
updateConstraints()
tintColorDidChange()
UIViewController
traitCollectionDidChange(_:)
updateViewConstraints()
viewWillLayoutSubviews()
viewDidLayoutSubviews()
UIPresentationController
traitCollectionDidChange(_:)
containerViewWillLayoutSubviews()
containerViewDidLayoutSubviews()
4. CGColor适配
iOS13中UIColor是动态变化的,但是对于CGColr,CALayer层面上的Color来说,还是只是单一颜色,我们只能在模式变化的时候的监听方法中适配。
方式一: 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];
/**
*CGColor适配
*iOS13后,UIColor能够表示动态颜色,但是CGColor依然只能表示一种颜色。所以对于CALayer对象只能在traitCollectionDidChange方法中进行改变
*/
if (@available(iOS 13.0, *)) {
if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) {
//修改label的边框色
self.layerLabel.layer.borderColor = KTestColor.CGColor;
if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
NSLog(@"************* iOS13 暗黑模式");
}else{
NSLog(@"############# iOS13 普通模式");
}
}
}
}
⚠️⚠️⚠️!!! 设置layer颜色都是在traitCollectionDidChange中,意味着如果没有发生模式切换,layer将会没有颜色,需要设置一个基本颜色
5. 图片适配
图片适配类似于color.seet的创建,首先创建一个image set ,设置模式,两种模式放入不同的图片
使用
[_logoImage setImage:[UIImage imageNamed:@"icon_logo"]];
6. 其他
1. 禁止暗黑模式,全局关闭暗黑模式
1.在Info.plist
文件中,添加UIUserInterfaceStyle
key 名字为 User Interface Style
值为String,
将UIUserInterfaceStyle
key 的值设置为Light
如图
2. 模式切换时打印log
模式切换时自动打印log,就不需要我们一次又一次的执行po命令了
在Xcode菜单栏Product
->Scheme
->Edit Scheme
选择Run
->Arguments
->Arguments Passed On Launch
添加以下命令即可
-UITraitCollectionChangeLoggingEnabled YES
3. 强行设置App模式
当系统设置为Light Mode
时,对某些App的个别页面希望一直显示Dark Mode
下的样式,这个时候就需要强行设置当前ViewController
的模式了
// 设置当前view或viewCongtroller的模式
@property(nonatomic) UIUserInterfaceStyle overrideUserInterfaceStyle;
使用
// 设置为Dark Mode即可
[self setOverrideUserInterfaceStyle:UIUserInterfaceStyleDark];
⚠️ 注意!!!
当我们强行设置当前viewController
为Dark Mode
后,这个viewController
下的view
都是Dark Mode
由这个ViewController present
出的ViewController
不会受到影响,依然跟随系统的模式
要想一键设置App下所有的ViewController
都是Dark Mode
,请直接在Window
上执行overrideUserInterfaceStyle
对window.rootViewController
强行设置Dark Mode
也不会影响后续present
出的ViewController
的模式
4. NSAttributedString优化
对于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];