Categories
程式開發

iOS暗黑模式適配的完美解決方案


一 背景

在2019WWDC的開場演講中,蘋果公佈了即將推出的iOS13 DarkMode的新特性。此新特性不僅可以在夜晚保護視力,而且對於使用OLED的最新一代設備而言,也可以幫助用戶節省電量消耗。不過此特性只支持iOS13以上的系統,為了給全系統所有用戶最好的體驗,研發出了一套皮膚主題框架,不僅可以全系統支持DarkMode,還可以擴展多套皮膚主題;

二 皮膚主題框架的誕生 BDPAppearance

iOS暗黑模式適配的完美解決方案 1

2.1 業務方使用方式

  • 目前系統所有控件及其Color屬性和Image屬性均已支持, 此處只列舉兩個例子:
// UIColor
view.backgroundColor = BDPAppearanceColor(@"C1");

// UIImage
imageV.image = BDPAppearanceImage(@"icon");

業務方只需如上使用簡單的API設置Color和Image,即可實現主題換膚;

2.2 實現原理

先來看一下BDPAppearance設計方案的架構圖

iOS暗黑模式適配的完美解決方案 2

在項目初始化時,會先加載當前主題所使用的色值資源到內存中,相關控件通過 BDPAppearanceColor 用色名去找出對應主題下的色值,實例化出UIColor對象,並賦值給這些相關控件;

而內置的UIImage圖片是存放在Assets中的,通過BDPAppearanceImage用圖片名去加載當前主題下的UIImage對象即可;

  • UIColor分類和UIImage分類

    可以看到圖中,給UIColor分類添加了ColorName屬性: 通過BDPAppearanceColor方式獲取UIColor實例對象時,通過分類會記錄當前Color對象所使用的色號名;

    同理,通過BDPAppearanceImage獲取UIImage時,通過分類會記錄當前Image對象所使用的圖片名;

  • changeTheme刷新主題

    每一個控件初始化添加到父視圖的時候,在- (void)didMoveToSuperview的時機將其添加到NSHashTable中, 點擊切換主題時,通過NSHashTable拿到當前視圖樹上所有的視圖控件,取出控件屬性中的UIColor和UIImage, 判斷其colorName和imageName是否有值,有值即代表當前控件需要適配主題,則用此colorName或者imageName去加載當前主題的新色值和新圖片,重新賦值給當前控件即完成了主題切換。

三 設計思路

設計此皮膚主題框架的原則:

業務方在現有業務的基礎上以最低成本的方式進行適配:即只需更換獲取顏色和圖片的方式

那麼在基於上述原則的前提下,我們應該如何在切換主題時,讓所有的控件重新刷新主題呢?

在項目初期時,採用通知的方案:在didMoveToSuperview方法中給當前控件添加一個通知,當收到切換主題的通知時,則刷新當前控件的相關色值及圖片;

但是在做性能測試時,發現採用通知方式初始化,不僅會有一定CPU消耗,同時也會增加初始化的耗時,視圖層級越多,性能損耗越明顯,所以放棄了此方案; 我們的目的其實就是可以讓當前所有的視圖可以觸髮刷新邏輯,最終採用了NSHashTable弱持有控件的方案;

兩種方式性能測試數據:

以下數據均是測試20次以上取的平均值;

壓力測試環境:視圖層級1w個View:

真機iphone5s 初始化CPU消耗 初始化耗時
正常 50% 3312ms
HashTable 50% 3341ms
Notification 99% 5115ms

由以上測試數據得出: 在上萬個視圖量級下, HashTable 性能是遠遠優於通知的方式

四 業界開源框架對比

以下是目前業界GitHub排名靠前的開源庫對比:

iOS暗黑模式適配的完美解決方案 3

同時對比iOS13系統API適配的方式:

UIColor *dynamicColor = (UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traitCollection) {
        if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
            return (UIColor blackColor);
        } else {
            return (UIColor whiteColor);
        }
    });

view.backgroundColor = dynamicColor;

對比可以看出:BDPAppearance使用方式是在保留RD開發習慣上的基礎上最接近系統方式的,改動代碼量是最小的;

五 客戶端整體主題通信設計

百度App涉及到主題相關模塊技術形態有:NA、H5、RN、HN等,而在多種技術形態下主題模式又是如何通信呢?可以參考下邊這張圖:

iOS暗黑模式適配的完美解決方案 4

如圖所示:

  • 初始化WKWebView時採用的方案:在UserAgent中拼接Key-Value的方式初始化WebView,達到在渲染時,最早時機拿到主題模式;
  • 主題變化通信採用的方案:數據通道和端能力,其本質是JS交互;

六 項目工程色值配置

  • 6.1 端內色值表的管理

整個百度App涉及近百個業務,組件近三百個,色號表的管理也顯得尤為重要;

每個組件內部所使用到的色號僅僅是有限個, 如果所有組件用到的色值都統一放入一個色值表中管理,顯然是不合理的,不利於解耦也更不利於組件化輸出;那麼最優的色值管理方式是什麼呢?

iOS暗黑模式適配的完美解決方案 5

如上圖所示,組件各自管理自己所需的色值表,在項目編譯時,會通過腳本將所有組件的色值表進行色值去重後,合併成一個總的色值表存儲在Themes倉庫下,然後初始化主題資源時讀取Themes裡邊總的色值表即可; 此種處理方式則達到了組件間解耦的目的;

  • 6.2 Sketch插件:ThemeMeasure

早期的開發中,UE出圖都是用的Sketch導出HTML格式標註圖,而根據百度App iOS相關CRD、FE 對實現技術的選型及配合要求,UE 需要提供並維護一套NA+H5 色表,標註界面時標顏色的編號而非色值。為了達到此種效果,我們同期研發出了Sketch插件,可以在標註界面直接顯示出色號,解決了UE標註色號的痛點,大大提高了效率; 如下圖:

iOS暗黑模式適配的完美解決方案 6

此插件共包含三種能力:

  • 1.多種便捷方式助力色號標註
    • Theme Measure 可以讓設計師根據系統的推薦選擇色號進行標註,還有貼心的批量標註嚮導,改變了過去設計師手動寫色號進行標註的方式
  • 2.一鍵轉換深色、夜間等主題
    • Theme Measure 能夠讓設計師一鍵將默認主題轉換為深色、夜間或者其他主題(需要有相應的色值數據,正確的色號標註),改變了過去設計師需要手動逐一調整產出深色、夜間設計稿的方式,大幅提升設計師對多主題適配的工作效率及體驗
  • 3.熟悉的標註導出方式,所有標註均在一處
    • 還是使用熟悉的工具導出標註,色號、佈局、字號等標註均在同一個 HTML 文檔內,改變了過去需要額外提供一份色號標註的方式,提升設計與研發的協同效率與體驗

在整套皮膚主題機制下業務方僅僅花了不到兩週的時間即完成了整個手百的主題適配,也從側面證實此框架的優點:輕量級,使用成本低;

資源配置同時也支持雲端下發,可動態新增多種主題;

七 支持自動跟隨系統主題變化以及動畫切換

八 總結

本文主要從皮膚主題框架實現、色值表的管理以及配套工具鍊等方面詳細的介紹了百度App iOS暗黑模式的適配,歡迎業內的朋友一起交流學習;

本文轉載自公眾號百度App技術。

原文鏈接

https://mp.weixin.qq.com/s?__biz=MzUxMzk2ODI1NQ==&mid=2247483925&idx=1&sn=90a668648fa9c57e8b6a2adac0556bb7&chksm=f94c5305ce3bda13745d6fce34661dd3a5720958bae5376a34de8087b3d6da508649c752ac0c&scene=27#wechat_redirect