[toc]
前言
本文來自拉勾網(wǎng)課程整理
從 iOS 13
開始,用戶可以從系統(tǒng)級別來把外觀模式改成深色模式(Dark mode
)。與原有的淺色模式(Light mode
)相比,使用深色模式具有以下幾大優(yōu)點:
- 由于減少發(fā)光,使用深色模式能大幅減少電量的消耗,延長
iPhone
的續(xù)航能力; - 對視力不佳或者與對強光敏感的用戶更為友好,為他們提供更好的可視性;
- 在暗光環(huán)境下,讓用戶使用手機(jī)時更舒服。
那么,我們的 App
怎樣才能在支持深色模式呢?下面我將結(jié)合咱們的項目案例 Moments App
來介紹下。
iOS 語義色
對于深色模式的支持,蘋果推薦使用語義化顏色(Semantic colors
)來進(jìn)行適配。什么叫語義化顏色呢?語義化顏色是我們根據(jù)用途來定義顏色的名稱,例如使用在背景上的顏色定義為background
,主文本和副文本的顏色分別定義為primaryText
和secondaryText
。UI
可以通過語義色來靈活地適配用戶所選擇的外觀模式
,比如背景在淺色模式
下顯示為白色
,而在深色模式
下顯示為黑色
。
為了簡化深色模式的適配過程,蘋果公司提供了具有語義的系統(tǒng)色(System colors
)和動態(tài)系統(tǒng)色(Dynamic system colors
)供我們使用。
上圖是蘋果開發(fā)者網(wǎng)站提供的一個iOS
系統(tǒng)色,有藍(lán)色、綠色、靛藍(lán)、橙色、黃色
等,它們在淺色模式和深色模式下會使用到不同的顏色值。比如藍(lán)色,在淺色模式下,它的 RGB
分別是 0、122、255
,在深色模式下則分別為10、132、255
。這樣就能保證系統(tǒng)藍(lán)色在不同的外觀模式的背景顏色上都能清晰顯示
。
上圖顯示是 iOS
系統(tǒng)提供的動態(tài)系統(tǒng)色的定義。它們都是通過用途來定義各種顏色的名稱。例如Label
用于主標(biāo)簽文字的顏色,而 Secondary label
用于副標(biāo)簽文字的顏色,使用它們就能自動支持不同的外觀模式了。
Moments App 的語義色
為了增強品牌效果,我們一般都會為 App
單獨定義一組語義色。下面以 Moments App
為例看看如何在代碼中定義語義色。
根據(jù) 07 講的設(shè)計規(guī)范,我們在 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)
}
}
我們?yōu)?code>UIColor定義了一個類型擴(kuò)展(Extension
)。為了調(diào)用時具有命名空間,我們在這個擴(kuò)展里定義了一個名叫DesignKitPalette
的內(nèi)嵌枚舉類型(Nested enum
),然后定義了一個靜態(tài)屬性來引用該枚舉。
首先,我們一起看看DesignKitPalette
兩個公用的方法。第一個是func dynamicColor(light: UIColor, dark: UIColor) -> UIColor
,在該方法里面,我們根據(jù)用戶當(dāng)前選擇的userInterfaceStyle
來返回對應(yīng)的深色或者淺色。
第二個方法是通過類型擴(kuò)展來為UIColor
類型添加了一個初始化函數(shù)(構(gòu)造函數(shù))。該初始化函數(shù)接收一個Int
類型的參數(shù),這個參數(shù)保存了一個十六進(jìn)制
的值。函數(shù)內(nèi)部從hex
里面取出分別表示紅色、綠色和藍(lán)色的R、G
和B
的值,例如傳入的hex
是0x0770e3
,那么R、G
和B
的值是分別是07、70
和e3
, 然后把這些值傳遞給原有的init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat)
初始化函數(shù)來生成一個UIColor
的實例。
有了這兩個函數(shù)以后,我們就可以很方便地定義設(shè)計規(guī)范里面的各種顏色了。具體來說,只需要把淺色和深色傳遞給語義色的屬性就可。比如,我們的語義色primary
所對應(yīng)的淺色和深色的十六進(jìn)制分別是0x0770e3
和0x6d9feb
,那么我們就可以通過這兩個值來生成一個支持動態(tài)顏色的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
取出相應(yīng)的語義色并賦值給類型為UIColor
的屬性即可。
測試語義色
當(dāng)我們的App
使用了語義色以后,要經(jīng)常在淺色和深色模式之間來回切換,加以測試,及時發(fā)現(xiàn)問題解決問題。要不然在開發(fā)過程中可能會因為不小心引入影響可讀性的Bug
,從而降低用戶體驗。幸運的是,iOS
的 Simulator
為我們提供了一組快捷鍵Command + Shift + A
來快速切換外觀模式。下面是 Moments App
在不同外觀模式下運行的效果。
從視頻上你可以看到,當(dāng)我按下快捷鍵Command + Shift + A
的時候 Moments App
在淺色和深色模式之間自動來回切換。這樣能幫我們快速檢查界面上文本的可讀性。
總結(jié)
在這一講中我介紹了如何通過語義色來靈活支持不同的外觀模式,同時以 Moments App
為例子介紹了如何通過UIFont
的擴(kuò)展來自定義語義色。
當(dāng)我們的 App
使用了語義色以后,還需要注意以下幾點。
- 不要把深色模式等于黑夜模式或者夜間模式,支持深色模式的
App
在正常光線的環(huán)境下也要為用戶提供良好的視覺舒適度。 -
App
應(yīng)該從系統(tǒng)設(shè)置里面讀取外觀模式的信息,而不是讓用戶在App
里面進(jìn)行單獨配置。 - 在開發(fā)過程中,要經(jīng)常切換外觀模式來測試
App
。 - 要在設(shè)置 App->輔助功能->顯示與字體大小頁面中修改降低透明度和增強對比度開關(guān),檢查深色內(nèi)容在黑色背景下的可讀性。
源碼地址: