[toc]
前言
本文来自拉勾网课程整理
从 iOS 13
开始,用户可以从系统级别来把外观模式改成深色模式(Dark mode
)。与原有的浅色模式(Light mode
)相比,使用深色模式具有以下几大优点:
- 由于减少发光,使用深色模式能大幅减少电量的消耗,延长
iPhone
的续航能力; - 对视力不佳或者与对强光敏感的用户更为友好,为他们提供更好的可视性;
- 在暗光环境下,让用户使用手机时更舒服。
那么,我们的 App
怎样才能在支持深色模式呢?下面我将结合咱们的项目案例 Moments App
来介绍下。
iOS 语义色
对于深色模式的支持,苹果推荐使用语义化颜色(Semantic colors
)来进行适配。什么叫语义化颜色呢?语义化颜色是我们根据用途来定义颜色的名称,例如使用在背景上的颜色定义为background
,主文本和副文本的颜色分别定义为primaryText
和secondaryText
。UI
可以通过语义色来灵活地适配用户所选择的外观模式
,比如背景在浅色模式
下显示为白色
,而在深色模式
下显示为黑色
。
为了简化深色模式的适配过程,苹果公司提供了具有语义的系统色(System colors
)和动态系统色(Dynamic system colors
)供我们使用。
上图是苹果开发者网站提供的一个iOS
系统色,有蓝色、绿色、靛蓝、橙色、黄色
等,它们在浅色模式和深色模式下会使用到不同的颜色值。比如蓝色,在浅色模式下,它的 RGB
分别是 0、122、255
,在深色模式下则分别为10、132、255
。这样就能保证系统蓝色在不同的外观模式的背景颜色上都能清晰显示
。
上图显示是 iOS
系统提供的动态系统色的定义。它们都是通过用途来定义各种颜色的名称。例如Label
用于主标签文字的颜色,而 Secondary label
用于副标签文字的颜色,使用它们就能自动支持不同的外观模式了。
Moments App 的语义色
为了增强品牌效果,我们一般都会为 App
单独定义一组语义色。下面以 Moments App
为例看看如何在代码中定义语义色。
根据 07 讲的设计规范,我们在 DesignKit
组件里面自定义了一组语义色,具体代码如下:
public extension UIColor {
static let designKit = DesignKitPalette.self
enum DesignKitPalette {
public static let primary: UIColor = dynamicColor(light: UIColor(hex: 0x0770e3), dark: UIColor(hex: 0x6d9feb))
public static let background: UIColor = dynamicColor(light: .white, dark: .black)
public static let secondaryBackground: UIColor = dynamicColor(light: UIColor(hex: 0xf1f2f8), dark: UIColor(hex: 0x1D1B20))
public static let tertiaryBackground: UIColor = dynamicColor(light: .white, dark: UIColor(hex: 0x2C2C2E))
public static let line: UIColor = dynamicColor(light: UIColor(hex: 0xcdcdd7), dark: UIColor(hex: 0x48484A))
public static let primaryText: UIColor = dynamicColor(light: UIColor(hex: 0x111236), dark: .white)
public static let secondaryText: UIColor = dynamicColor(light: UIColor(hex: 0x68697f), dark: UIColor(hex: 0x8E8E93))
public static let tertiaryText: UIColor = dynamicColor(light: UIColor(hex: 0x8f90a0), dark: UIColor(hex: 0x8E8E93))
public static let quaternaryText: UIColor = dynamicColor(light: UIColor(hex: 0xb2b2bf), dark: UIColor(hex: 0x8E8E93))
static private func dynamicColor(light: UIColor, dark: UIColor) -> UIColor {
return UIColor { $0.userInterfaceStyle == .dark ? dark : light }
}
}
}
public extension UIColor {
convenience init(hex: Int) {
let components = (
R: CGFloat((hex >> 16) & 0xff) / 255,
G: CGFloat((hex >> 08) & 0xff) / 255,
B: CGFloat((hex >> 00) & 0xff) / 255
)
self.init(red: components.R, green: components.G, blue: components.B, alpha: 1)
}
}
我们为UIColor
定义了一个类型扩展(Extension
)。为了调用时具有命名空间,我们在这个扩展里定义了一个名叫DesignKitPalette
的内嵌枚举类型(Nested enum
),然后定义了一个静态属性来引用该枚举。
首先,我们一起看看DesignKitPalette
两个公用的方法。第一个是func dynamicColor(light: UIColor, dark: UIColor) -> UIColor
,在该方法里面,我们根据用户当前选择的userInterfaceStyle
来返回对应的深色或者浅色。
第二个方法是通过类型扩展来为UIColor
类型添加了一个初始化函数(构造函数)。该初始化函数接收一个Int
类型的参数,这个参数保存了一个十六进制
的值。函数内部从hex
里面取出分别表示红色、绿色和蓝色的R、G
和B
的值,例如传入的hex
是0x0770e3
,那么R、G
和B
的值是分别是07、70
和e3
, 然后把这些值传递给原有的init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat)
初始化函数来生成一个UIColor
的实例。
有了这两个函数以后,我们就可以很方便地定义设计规范里面的各种颜色了。具体来说,只需要把浅色和深色传递给语义色的属性就可。比如,我们的语义色primary
所对应的浅色和深色的十六进制分别是0x0770e3
和0x6d9feb
,那么我们就可以通过这两个值来生成一个支持动态颜色的UIColor
对象,代码如下所示。
public static let primary: UIColor = dynamicColor(light: UIColor(hex: 0x0770e3), dark: UIColor(hex: 0x6d9feb))
有了这些定义以后,我们可以在代码中很方便地使用它们。代码如下:
label.textColor = UIColor.designKit.primaryText
view.backgroundColor = UIColor.designKit.background
可以看到,我们可以通过UIColor.designKit
取出相应的语义色并赋值给类型为UIColor
的属性即可。
测试语义色
当我们的App
使用了语义色以后,要经常在浅色和深色模式之间来回切换,加以测试,及时发现问题解决问题。要不然在开发过程中可能会因为不小心引入影响可读性的Bug
,从而降低用户体验。幸运的是,iOS
的 Simulator
为我们提供了一组快捷键Command + Shift + A
来快速切换外观模式。下面是 Moments App
在不同外观模式下运行的效果。
从视频上你可以看到,当我按下快捷键Command + Shift + A
的时候 Moments App
在浅色和深色模式之间自动来回切换。这样能帮我们快速检查界面上文本的可读性。
总结
在这一讲中我介绍了如何通过语义色来灵活支持不同的外观模式,同时以 Moments App
为例子介绍了如何通过UIFont
的扩展来自定义语义色。
当我们的 App
使用了语义色以后,还需要注意以下几点。
- 不要把深色模式等于黑夜模式或者夜间模式,支持深色模式的
App
在正常光线的环境下也要为用户提供良好的视觉舒适度。 -
App
应该从系统设置里面读取外观模式的信息,而不是让用户在App
里面进行单独配置。 - 在开发过程中,要经常切换外观模式来测试
App
。 - 要在设置 App->辅助功能->显示与字体大小页面中修改降低透明度和增强对比度开关,检查深色内容在黑色背景下的可读性。
源码地址:
定义语义色的文件地址:https://github.com/lagoueduCol/iOS-linyongjian/blob/main/Frameworks/DesignKit/src/Color/UIColorExtensions.swift