14 | 功能組件:如何使用語義色,支持深色模式?

[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,主文本和副文本的顏色分別定義為primaryTextsecondaryTextUI 可以通過語義色來靈活地適配用戶所選擇的外觀模式,比如背景在淺色模式下顯示為白色,而在深色模式下顯示為黑色

為了簡化深色模式的適配過程,蘋果公司提供了具有語義的系統(tǒng)色(System colors)和動態(tài)系統(tǒng)色(Dynamic system colors)供我們使用。

971d2717460845907ab6e3f981fef03e

上圖是蘋果開發(fā)者網(wǎng)站提供的一個iOS 系統(tǒng)色,有藍(lán)色、綠色、靛藍(lán)、橙色、黃色等,它們在淺色模式和深色模式下會使用到不同的顏色值。比如藍(lán)色,在淺色模式下,它的 RGB 分別是 0、122、255,在深色模式下則分別為10、132、255。這樣就能保證系統(tǒng)藍(lán)色在不同的外觀模式的背景顏色上都能清晰顯示

2e11c54d35517ed0457fa42e3fd08813

上圖顯示是 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、GB的值,例如傳入的hex0x0770e3,那么R、GB的值是分別是07、70e3, 然后把這些值傳遞給原有的init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat)初始化函數(shù)來生成一個UIColor的實例。

有了這兩個函數(shù)以后,我們就可以很方便地定義設(shè)計規(guī)范里面的各種顏色了。具體來說,只需要把淺色和深色傳遞給語義色的屬性就可。比如,我們的語義色primary所對應(yīng)的淺色和深色的十六進(jìn)制分別是0x0770e30x6d9feb,那么我們就可以通過這兩個值來生成一個支持動態(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 ,從而降低用戶體驗。幸運的是,iOSSimulator 為我們提供了一組快捷鍵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)容在黑色背景下的可讀性。

源碼地址:

定義語義色的文件地址:https://github.com/lagoueduCol/iOS-linyongjian/blob/main/Frameworks/DesignKit/src/Color/UIColorExtensions.swift

0b4b5e3b026981a72a605d427c46c340
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容