最近公司业务需求要更换APP主题。最开始是一个地方一个地方去改,并且项目中不少老代码是用xib写的,习惯纯代码编程的我改的很难受。并且之后指不定要再次更改主题。html
因而我定义了几个主要颜色的宏,代码中只要是设置颜色的地方就用宏。这样只须要改一次,当要切换主题的时候直接对宏进行更改就好了。ios
结合已作好的切换主题功能,再加上一个暗黑模式判断,若是当前是暗黑模式就用A套色值,若是不是就用B套色值,这样就实现了暗黑模式的适配了。编程
.bash
代码中只要是设置颜色的地方就用定义好的颜色。(下面个别宏只是个人项目场景中会使用到的,并不适用于全部APP,可自行针对本身的项目定义。有些颜色两种模式下没有变更)app
/// 暗黑模式 YES是
#define CKDarkMode @available(iOS 13.0, *) && UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark
// MARK: - 十六进制颜色
#define HexOf(rgbValue) Hex_A(rgbValue,1.0)
#define Hex_A(rgbValue,a) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:a]
// MARK: - 用全局变量设置背景、文字,能够优雅的主题切换 (取全局惟一性的名称,便于维护;最前面的优先级最高)
#define Color_Bg CKDarkMode?HexOf(0x191C32):HexOf(0xf4f4f4) //背景主题颜色 黑色/白色
#define Color_ContView CKDarkMode?HexOf(0x1D213B):HexOf(0xffffff) //内容、cell颜色 深蓝色/白色 若是背景和cell颜色同样,就都用这个
#define Color_Title CKDarkMode?HexOf(0xFFFFFF):HexOf(0x393939) //主文字颜色 白色/黑色
#define Color_Subtitle CKDarkMode?HexOf(0x999999):HexOf(0x999999) //副文字颜色 浅白色/灰色
#define Color_Green CKDarkMode?HexOf(0x45C98F):HexOf(0x45C98F) //绿涨 (行情、交易)
#define Color_Red CKDarkMode?HexOf(0xEF0C47):HexOf(0xEF0C47) //红跌 (行情、交易)
//
#define Color_NavBg CKDarkMode?HexOf(0x1D213B):HexOf(0xffffff) //导航栏背景颜色
#define Color_NavTitle CKDarkMode?HexOf(0xFFFFFF):HexOf(0x393939) //导航栏标题颜色
#define Color_TabbarBg CKDarkMode?HexOf(0x1D213B):HexOf(0xffffff) //标签栏背景颜色
#define Color_Selected CKDarkMode?HexOf(0x46CA8F):HexOf(0x46CA8F) //绿色 (按钮选中、已认证状态的颜色)
#define Color_Line CKDarkMode?HexOf(0x191C32):HexOf(0xf4f4f4) //分割线
#define Color_DarkGray CKDarkMode?HexOf(0x333333):HexOf(0x333333) //深灰色
#define Color_Gray CKDarkMode?HexOf(0x666666):HexOf(0x666666) //灰色
#define Color_LightGray CKDarkMode?HexOf(0x999999):HexOf(0x999999) //浅灰色
#define Color_InputBg CKDarkMode?HexOf(0x191C32):HexOf(0xf4f4f4) //输入框背景颜色
#define Color_DarkBlue CKDarkMode?HexOf(0x191C32):HexOf(0x191C32) //深蓝色 (特殊颜色)
#define Color_HalfTitle CKDarkMode?Hex_A(0x999999, 0.5):Hex_A(0x999999, 0.5);//半透明文字 色值是副标题的一半
复制代码
若是想关闭暗黑模式,直接设置:ide
#define CKDarkMode NO函数
.post
self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark
去作判断,可是有些类并无UITraitCollection这个属性,不少地方报错。解决方案:ui
改用UITraitCollection.current属性来获取当前App的颜色模式。spa
bt.layer.borderColor = Color_Selected.CGColor;
复制代码
报错:
Incompatible operand types ('UIColor * _Nonnull' and 'CGColorRef _Nonnull' (aka 'struct CGColor *'))
解决方案:
UIColor *color = Color_Selected;
bt.layer.borderColor = color.CGColor;
复制代码
解决方案:
在页面中添加通知,获取到切换主题的通知后从新刷新一下页面颜色(相似于项目中的国际化通知处理逻辑)
解决方案:
添加一个UIStatusBarStyle变量记录主题状态栏颜色,这样能够不用在控制器内作太多额外的判断。若是用 @available(iOS 13.0, *) 去作判断,需求变动后还要每一个地方都去改动代码。用了这种方式,后面即便更改了主题或者关闭了暗黑模式,也不用一一去改代码;也能够经过上面定义的宏CKDarkMode作判断,关闭暗黑模式时只需把CKDarkMode设置为NO就行
UIStatusBarStyle _themeStatusBarStyle;//记录主题状态栏颜色
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
_themeStatusBarStyle = [UIApplication sharedApplication].statusBarStyle;
// 设置状态栏颜色为白色
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
}
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
// 恢复状态栏颜色为主题颜色
[UIApplication sharedApplication].statusBarStyle = _themeStatusBarStyle;
}
复制代码
'currentTraitCollection' is only available on iOS 13.0 or newer
解决方案:使用UIColor扩展。
999+的警告有点影响代码视觉体验,后面应该会改用扩展的方式。若是有更好的解决方案请在下方留言。
.
UIView 和 UIViewController 、UIScreen、UIWindow
都已经听从了UITraitEnvironment
这个协议,所以这些类都拥有一个叫作 traitCollection
的属性,在这些类中,咱们能够这样去判断当前 App 的颜色模式:BOOL isDark = (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark);
UITraitCollection.current
这个属性来获取当前 App 的颜色模式。在 Info.plist 文件中,添加 key 为 User Interface Style,类型为 String,value 设置为 Light (Dark)
便可,若是从新打开就把这条设置删除。(这种方式是在APP整个生命周期内关闭了暗黑模式;上面的设置#define CKDarkMode NO
只是用代码作了判断并只在用了宏的地方起做用)。
UIView、UIViewController 、UIWindow
有了一个 overrideUserInterfaceStyle
的新属性,能够覆盖系统的模式。单个页面或视图关闭暗黑模式,设置 overrideUserInterfaceStyle
为对应的模式,强制限制该视图与其子视图以设置的模式进行展现,不跟随系统模式改变进行改变。
self.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
设置此属性会影响当前view / viewController / window
以及它下面的任何内容。
若是你但愿一个子视图监听系统的模式,请将 overrideUserInterfaceStyle
属性设置为.unspecified
。
.
除了个人这种实现方案,还有其余方案能够适配暗黑模式:
+(UIColor *)generateDynamicColor:(UIColor *)lightColor darkColor:(UIColor *)darkColor{
if (@available(iOS 13.0, *)) {
UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traitCollection) {
if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleLight) {
return lightColor;
}else {
return darkColor;
}
}];
return dyColor;
}else{
return lightColor;
}
}
复制代码
问题:
这种写法要在每一个使用的地方分别传一个普通模式的颜色和暗黑模式的颜色,不方便维护。
解决方案:
能够定义几个经常使用颜色函数,特殊的场景就用上面的方法,这样就不须要在每一个地方都控制颜色值了。
+(UIColor *)ContViewColor{
if (@available(iOS 13.0, *)) {
UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traitCollection) {
if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
return ColorA;
}else {
return ColorB;
}
}];
return dyColor;
}else{
return ColorB;
}
}
复制代码
Images.xcassets
中定义几种经常使用颜色,并为颜色再配置一个用于暗黑模式的对应的颜色:Images.xcassets
中配置不一样模式下的图片,当你设置为暗黑模式后就会自动显示对应的图片LaunchScreen.storyboard
能够像普通的图片那样针对深色模式设置另外的一张图片
UIColor *resolvedColor = [[UIColor labelColor] resolvedColorWithTraitCollection:self.view.traitCollection];
label.layer.borderColor = resolvedColor.CGColor;
复制代码
iOS 13前 的 UIActivityIndicatorViewStyle:
typedef NS_ENUM(NSInteger, UIActivityIndicatorViewStyle) {
UIActivityIndicatorViewStyleWhiteLarge,
UIActivityIndicatorViewStyleWhite,
UIActivityIndicatorViewStyleGray,
};
复制代码
iOS 13后,因为暗黑模式,上述三个属性都被废弃,建议使用以下 style:
typedef NS_ENUM(NSInteger, UIActivityIndicatorViewStyle) {
UIActivityIndicatorViewStyleMedium,
UIActivityIndicatorViewStyleLarge,
UIActivityIndicatorViewStyleWhiteLarge API_DEPRECATED_WITH_REPLACEMENT("UIActivityIndicatorViewStyleLarge",
UIActivityIndicatorViewStyleWhite API_DEPRECATED_WITH_REPLACEMENT("UIActivityIndicatorViewStyleMedium",
UIActivityIndicatorViewStyleGray API_DEPRECATED_WITH_REPLACEMENT("UIActivityIndicatorViewStyleMedium",
};
复制代码
在 iOS 13 以前,状态栏的样式的枚举值也带有着明显的颜色倾向,UIStatusBarStyleDefault、UIStatusBarStyleLightContent
。
如今,状态栏的 default 样式会根据当前的模式展现不一样的颜色,而原有的 lightContent
样式则新增一个 darkContent
的样式与之对应。
typedef NS_ENUM(NSInteger, UIStatusBarStyle) {
UIStatusBarStyleDefault = 0, // Automatically chooses light or dark content based on the user interface style
UIStatusBarStyleLightContent = 1, // Light content, for use on dark backgrounds
UIStatusBarStyleDarkContent = 3, // Dark content, for use on light backgrounds
};
复制代码
.
命名时要保证这个名字的全局惟一性,避免和项目中其余命名雷同,这样能够保证全局搜索时搜索到的结果只有你想搜索的内容,便于维护。例如你取名RedColor,会搜索到不少其余没用的信息。这种命名思路也能够用在其余地方。
除了改背景颜色、文字颜色,还须要替换图标、图片,这个须要UI配合切图。
How To Adopt Dark Mode In Your iOS App
DarkMode1
DarkMode2
DarkMode3