苹果在10.14版本中添加了主题设置功能,用户可以将主题切换为Dark Mode,切换后系统应用都会自动变成深色。那么如何让我们的应用也能自动适配Dark Mode呢?
暗黑模式的适配主要包括颜色适配和图片适配。
对于颜色的适配,官方文档介绍了两种方法,一种是使用系统的语义颜色(semantic colors)代替固定的颜色值。例如[NSColor labelColor]
,[NSColor windowBackgroundColor]
使用这类语义颜色系统会自动根据当前的主题使用合适的颜色值进行渲染。也就是说它们表现出的实际颜色会随着当前主题变化而变化。所以该方法很简单,工作量也少,但是缺点也很明显:系统提供的颜色有限,不能满足所有应用场景。
第二种方法是使用color asset,在Assets.xcassets中创建Color Set,设置不同主题下不同的颜色值。
Any Appearance:macOS Mojave 10.14.2系统以下版本的颜色值;
Light Appearance: 浅色模式下的颜色值;
Dark Appearance:深色模式下的颜色值;
在应用中通过color name获取颜色:
//该方法只能用于macOS 10.13及以上的系统
NSColor *color = [NSColor colorNamed:@"text_color"];
该方法和系统的语义颜色一样,会根据当前的主题自动适配。但是有个前提:直接使用NSColor设置颜色值。例如:
NSColor *color = [NSColor colorNamed:@"text_color"];
[textField setTextColor:color];
如果是使用CGColor设置颜色值,则不会有效果。例如设置NSViewController的背景颜色:
self.view.wantsLayer = YES;
self.view.layer.backgroundColor = [NSColor whiteColor].CGColor;
当切换主题时,系统会让window和view重新绘制,对于NSViewController, 会自动调用viewWillLayout
和viewDidLayout
方法,对于NSView,会自动调用drawRect:
方法。官方文档的意思:如果使用CGColor,需要在以上回调方法中设置,例如:
- (void)viewWillLayout{
[super viewWillLayout];
NSColor *color = [NSColor colorNamed:@"bg_color"];
self.view.wantsLayer = YES;
self.view.layer.backgroundColor = color.CGColor;
}
但是经过测试,这种操作没有效果!!!NSViewController的背景颜色不会随着主题变化而变化!
解决办法:
在回调的方法中根据当前的主题手动设置对应的颜色值。
那么如何判断当前是否是深色模式呢?
有的小伙伴可能会注意到NSAppearance.currentAppearance
这个方法,NSAppearance.currentAppearance
只能获取到应用启动时的系统主题,当主题更改后,其值不会动态更新。
正确方法:
/**
判断当前是否是暗黑主题
*/
+(BOOL)isDarkMode{
NSAppearance *apperance = NSApp.effectiveAppearance;
if (@available(macOS 10.14, *)) {
return [apperance bestMatchFromAppearancesWithNames:@[NSAppearanceNameDarkAqua,NSAppearanceNameAqua]] == NSAppearanceNameDarkAqua;
} else {
return YES;
}
return NO;
}
有了这个方法,就可以在回调方法中根据主题动态的设置背景颜色了。
- (void)viewWillLayout{
[super viewWillLayout];
self.view.wantsLayer = YES;
if([self isDarkMode]){
self.view.layer.backgroundColor = [NSColor colorWithHex:0x1C1F2B].CGColor;
}else{
self.view.layer.backgroundColor = [NSColor whiteColor].CGColor;
}
}
对于图片适配要轻松一点,目前还没有遇到什么坑,方法和color asset一样,在在Assets.xcassets中创建Image Set,不同的主题下设置不同的图片。
对于深色模式的适配,主要工作在于颜色的适配,颜色适配的场景主要有:NSViewController的背景颜色适配、控件颜色适配、自定义View的颜色适配。
(1)NSViewController背景颜色适配
创建一个BaseViewController,在viewWillLayout方法中统一设置view的背景颜色。其他NSViewController继承BaseViewController,并根据需要重写viewWillLayout方法。
/// BaseViewController.m
- (void)viewWillLayout{
[super viewWillLayout];
self.view.wantsLayer = YES;
if([self isDarkMode]){
self.view.layer.backgroundColor = [NSColor colorWithHex:0x1C1F2B].CGColor;
}else{
self.view.layer.backgroundColor = [NSColor whiteColor].CGColor;
}
}
(2)控件颜色适配
常见的控件颜色适配场景主要有:控件的背景色,控件的填充色,文本颜色等。其方法都是获取对应的颜色,然后进行设置,如果使用的是NSColor则可以直接设置颜色,如果是CGColor,则需要在系统回调的方法中设置。而获取颜色的方法封装在一个AppThemeHelper中,例如:
///AppThemeHelper.h
//获取固定的颜色值,在系统回调的方法中设置
+(NSColor *)getFixedMainBgColor{
if (@available(macOS 10.13, *)) {
if([self isDarkMode]){
return [NSColor colorWithHex:0x1C1F2B];
}else{
return [NSColor colorWithHex:0xFFFFFF];
}
} else {
return [NSColor colorWithHex:0xFFFFFF];
}
}
//获取文本颜色
+(NSColor *)getTitleColor{
if (@available(macOS 10.13, *)) {
return [NSColor colorNamed:@"title_color" bundle:[NSBundle mainBundle]];
} else {
return [NSColor whiteColor];
}
}
//设置控件的背景色
+(void)setLayerBackgroundFor:(NSView *) view{
view.wantsLayer = YES;
if (@available(macOS 10.13, *)) {
if([self isDarkMode]){
view.layer.backgroundColor = [NSColor colorWithHex:0x1C1F2B].CGColor;
}else{
view.layer.backgroundColor = [NSColor whiteColor].CGColor;
}
} else {
view.layer.backgroundColor = [NSColor whiteColor].CGColor;
}
}
(3)自定义View的颜色适配
自定义View的颜色适配和其他场景类似,不同点在于系统回调的方法是drawRect:
方法。
官方文档:
https://developer.apple.com/documentation/appkit/supporting_dark_mode_in_your_interface
StackOverflow:
https://stackoverflow.com/questions/51672124/how-can-dark-mode-be-detected-on-macos-10-14
~ 欢迎大家加入我们的知识星球,一起交流学习 ~
微信扫码加入:
点击链接加入:https://t.zsxq.com/n27YzRV