首頁>科技>

導語

關於深色模式, 58 同城 App 最近也做了很多探索和實踐。本文總結了 58 同城 App 深色模式適配實踐經驗與成果,希望對您有所啟發,讓你的 App 開發的更加賞心悅目。

前言

隨著 iOS 13 和 Android 10 的正式釋出,深色模式 (Dark Mode) 風靡一時。深色模式帶來了酷炫的介面,更讓人眼睛看起來非常舒服,如果用一個詞來總結深色模式,那就是“賞心悅目”!

目前業界已有不少App適配了深色模式,58 同城 iOS App 經過幾個版本的迭代,在幾個月前就已經上線深色模式。本文將通過深色模式適配緣由,深色模式適配設計目標,適配成果展示三方面,與大家分享 58 App 深色模式適配的經驗與成果。

為什麼適配深色模式

深色模式的適配,必然會帶來開發成本的增加。要為 58 App 這樣一個業務龐大、技術棧多樣的應用適配深色模式,並不是一件容易的工作。為什麼要適配深色模式,深色模式又能帶來怎樣的收益,是很多設計師、開發者、產品關心的問題。

1. 蘋果推薦

深色模式是 iOS 13 推出的新特性,深色模式為 iOS 系統和各種 app 帶來精美的深色配色方案。iOS 系統中,內建的 App 大都已適配深色模式,蘋果強烈推薦開發者為自己的 App 適配深色模式。

2. 提升使用者體驗

深色模式不僅為 App 帶來酷炫的深色配色方案,更有助於提升使用者體驗。在弱光環境下,深色模式的 App 讓你的眼睛看起來舒服,而且更專注於內容,同時也不會打擾周圍的人。

隨著 iOS、Android 在系統層級對深色模式的支援,許多 App 也已逐步適配深色模式,未來越來越多的使用者也將習慣並依賴於深色模式。試想一下,一個習慣了深色模式的使用者,在一個寧靜幽暗夜晚,突然切換到一個閃亮的 App,是多麼糟糕的體驗!

3. 業界趨勢

在蘋果推出深色模式之前,許多 App 業已支援夜間模式。谷歌在 Android 10 系統實現了對深色模式的支援,Flutter 1.12 也已完全支援 iOS 13 中的深色模式。隨著 iOS、Android 兩大手機作業系統對深色模式的完美支援,深色模式適配逐漸成為業界趨勢,為自己的 App 適配深色模式也成為一項必要的工作。

目前業界的許多應用,包括淘寶 App、百度 App、QQ、愛奇藝、優酷等都已適配深色模式,就連曾不忍心佔用使用者珍貴夜晚的微信,也已向用戶認慫,上線了深色模式。

4. 節省電量

深色模式不僅能為使用者帶來賞心悅目的體驗,還能夠節省電量,改善電池壽命。在 OLED 螢幕的手機(包括 iPhone 11 Pro, iPhone XS, and iPhone X)上,效果更加明顯。

OLED 螢幕通過關閉相應的畫素來產生純黑色顯示。這意味著在深色模式下,螢幕上的所有黑色區域都將被完全關閉,從而節省電池電量。

YouTube PhoneBuff 頻道釋出了一個實驗視訊,對比了 iPhone XS Max 在相同亮度下,淺色模式和深色模式下的電池使用情況。經過數小時的使用後,深色模式 iPhone 的電池電量剩餘 43%,明顯高於淺色模式 iPhone 的電池電量的 20%;當淺色模式的 iPhone 電量耗盡後,深色模式的 iPhone 電池電量還剩餘 30%。

適配設計目標

對於 58 App 這樣一個業務龐雜的大型App,適配深色模式並不是一件容易的工作。深色模式的適配涉及首頁、部落、熱議、招聘、租房、二手房、中古車、黃頁、passport 等諸多業務線。58 App 的技術棧也相當複雜,頁面包括 Navtive 頁面、H5 頁面、RN 頁面,都需要適配深色模式。另外版本迭代節奏比較快,平均三週一個版本,要在一兩個版本完成整個 App 的深色模式適配也不現實,我們通過幾個版本的迭代,目前主路徑頁面已經完成深色模式適配,對於暫時未能適配深色模式的頁面,通過自動新增蒙層,保障使用者體驗。為了減輕後續業務迭代成本,提高開發效率,我們設計樣式庫,逐步推廣標準化元件。58 App 的深色模式適配設計目標主要考慮了以下幾方面內容:

1. 如何應對複雜技術棧

58 App頁面從技術上可以分為三類,Native 頁面、RN 頁面、Web 頁面,三種頁面深色模式適配技術上相互獨立,卻又緊密聯絡,整體流程設計如下圖所示:

圖1 流程設計圖

首先三類頁面中,都存在由於開發資源和專案時間等因素限制,暫時未能適配深色模式的頁面。為保證這些頁面正常顯示,讓使用者獲得更好的使用者體驗,同時減輕業務線開發成本,由 Native 端統一新增蒙層。當 App 開啟深色模式開關時,如果頁面未適配深色模式,則自動新增蒙層;當跳轉到適配深色模式的頁面時,則移除蒙層。具體實現方案見“如何相容不能參與適配功能”小節,此處不再贅述。

下面分別介紹 Native 頁面、RN 頁面、Web 頁面深色模式適配技術方案:

Ø Native 頁面深色模式適配

為了相容暫時未能適配的頁面能夠正常顯示,我們通過 Runtime Method Swizzling 將頁面預設重置為淺色樣式。對於需要適配深色模式的頁面,開發者首先需要在 viewDidLoad 方法中重置頁面樣式,使當前頁面支援深色模式,示例程式碼如下:

- (void)viewDidLoad{ [super viewDidLoad]; if (@available(iOS 13.0, *)) {

在頁面支援深色模式後,接下來主要工作為修改檢視背景色,文字顏色以及圖片。為提高開發效率,同時考慮後續業務迭代擴充套件成本,我們開發了樣式庫,樣式庫實現了顏色與圖片的統一管理,規範了 UI 標準,為深色模式適配奠定了基礎,具體設計參見“如何應對後續業務迭代成本”小節。

樣式庫提供了簡單易用的 API,修改檢視背景色,程式碼示例如下:

view.backgroundColor = [StyleLib colorWithType:@"Primary_1"];

為 UIImage 設定背景圖片,示例如下:

UIImage* backImage = [StyleLib iconWithType:@"icon_back "];

Ø RN頁面深色模式適配

RN 頁面為統一的載體頁,在初始化時,Native 側通過跳轉協議或 Action 協議中相關引數,識別當初頁面是否支援深色模式,設定當前載體頁樣式。同時將當前 App 樣式,發訊息給 RN 側,渲染頁面。

RN 元件適配深色模式,示例如下:

const styles = StyleSheet.create( { text: { fontSize: 16, color: ‘black’ } }, { text: { color: ‘white’ } });//fontSize: 16, color: ‘white’< Text style={styles.text} >

RN 側同時監聽 App 顯示狀態,當 App 顯示狀態切換時,Native側將當前樣式傳送至 RN 側,RN 側重新整理渲染頁面,實現邏輯如下:

MYAPP . addListener( ‘custom_dark_mode’ , mode => { // 方案二:重啟RN應用 MYAPP . applyUpdate( ) // 方案三:重新整理RN應用 this . setState( { visible: false } ) ; setTimeout ( ( ) => { this . setState( { visible: true } ) ;}, 0 ) ; });

方案二:重啟RN應用。所有狀態丟失,⻚面重新渲染。

方案三:重新整理RN應用。原有狀態保留,⻚面重新渲染。

Ø Web頁面深色模式適配

Web 頁面同樣在初始化時,通過跳轉協議或 Action 協議引數,傳送給 Hybrid 載體頁,識別當初頁面是否支援深色模式,設定當前載體頁樣式。Web 頁實現相對簡單,自己能夠監聽 App 顯示狀態,使用如下:

function myFunction(x) { if (x.matches) { // 媒體查詢 MYAPP.action.toast('dark') } else { MYAPP.action.toast('light') }}var x = window.matchMedia("(prefers - color - scheme: dark)") // 執行時呼叫的監聽函式myFunction(x)// 狀態改變時新增監聽器 模式修改的時候觸發x.addListener(myFunction)

2. 如何應對大型 App 並行研發

58 App 迭代速度較快,通常三週一個版本。深色模式適配專案涉及首頁、部落、熱議、招聘、租房、二手房、中古車、黃頁、passport 等諸多業務線,其中每個業務線有自已的日常業務開發和資源限制,另外隨著 App 工廠專案的持續推進,一些基礎元件和通用業務可能需要平移到其他應用。如何處理 58 App 中多業務線並行研發,甚至基礎元件和通用業務的跨應用平移,都是在專案中要解決的問題。

為支撐多業務線並行研發,提高開發效率,深色模式適配框架分為基礎層、服務層、業務層三層,架構設計如下:

基礎層定義了樣式庫,主要包括顏色樣式庫、圖片樣式庫、主題庫,實現了圖片、顏色、主題的統一管理,提供了簡潔易用的 API,為深色模式適配奠定了基礎。

服務層主要包括蒙層管理、跳轉協議與 action 協議、控制開關。蒙層管理用於為暫時不支援深色模式的頁面自動新增蒙層,優化使用者體驗;跳轉協議、action 協議中定義了 RN 側、Web 側與 Native 端互動引數,用於控制頁面顯示樣式。

控制開關在多業務並行研發過程中發揮著重要作用,控制開關分為 App 級與頁面級兩個粒度,能夠靈活控制 App 及頁面樣式,為專案品質提供保障。專案開發初期,由於整體頁面適配覆蓋率較低,為保障使用者體驗,使用 App 級控制開關臨時關閉深色模式。隨時專案持續推進,整體效果碰到預期,我們再開啟 App 級控制開關,如果僅有個別頁面效果不佳,可以使用頁面級控制開關,暫時關閉。

58 App 中許多基礎元件及通用業務,要隨著 App 工廠專案平移到其他應用,在深色模式適配過程中,要考慮不同 App 間主題的相容有以及其他應用開發編譯環境相容。關於基礎元件主題樣式間的相容,可以參考“如何應對後續業務迭代成本”小節樣式庫的介紹。

在深色模式適配開發中,需要使用一些 iOS 13 新增的 API,如 setOverrideUserInterfaceStyle,如果不加編譯控制,在低版本 Xcode 中,會導致編譯失敗。58 App 中許多公共元件,需要平移至集團其他 App 中,為相容低版本 Xcode 編譯,在使用 iOS 13 API 時,增加以下編譯控制判斷:

#if defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 if (@available(iOS 13.0, *)) { self.overrideUserInterfaceStyle = UIUserInterfaceStyleLight; }#endif

3. 如何相容不能參與適配功能

對於 58 App 這樣大型複雜的應用,在短期內將所有的頁面完成深色模式適配不太現實。由於各業務線有自己的開發任務和資源限制等因素,部分頁面暫時不能支援適配。如何保障這些未適配的頁面能夠正常顯示,並且最大可能的提升深色模式下使用者體驗,是我們設計過程中思考的一個重要問題。

Ø 相容未適配深色模式的頁面正常顯示

Xcode 11預設開啟了深色模式,一些使用系統預設顏色的檢視,在切換到深色模式時,可能出現顯示異常,如下圖所示:

針對類似問題,蘋果官方提供了解決方案,暫時不能支援深色模式適配的控制器、檢視,可以將其 overrideUserInterfaceStyle 屬性,設定為 UIUserInterfaceStyleLight 樣式。解決方法看似簡單,但要推動所有業務線去修改頁面樣式卻並非易事,並且實施過程中還有可能遺漏。

為減輕業務線適配壓力,提高開發效率,經過專案組討論,最終我們選擇使用 Objective-C Runtime中的黑魔法 method swizzling 。通過 hook viewDidLoad 方法,在交換方法中,預設將 ViewController的overrideUserInterfaceStyle 屬性重置為淺色樣式,示例程式碼如下:

- (void)my_viewDidLoad{ if (@available(iOS 13.0, *)) { if(NO == [self isSystemController]){ self.overrideUserInterfaceStyle = UIUserInterfaceStyleLight; } } [self my_viewDidLoad];}

通過上述方案,確保即使未適配深色模式的頁面,在使用者切換到深色模式時,也能夠正常顯示。需要適配深色的模式的頁面,在相應的 viewDidLoad 方法中,重置 overrideUserInterfaceStyle 屬性為預設樣式即可。

在這裡需要注意的是,不能簡單粗暴的將所有的控制器重置為淺色樣式。專案中會使用圖片選擇、文件選擇等系統提供的功能,這些控制器已支援深色模式。為解決該問題,我們建立了名單管理機制,過濾掉那些以 UI 或下劃線開頭的系統類以及一些有特殊需求的業務類,使用預設樣式。

另一個需要說明的問題是,上述程式碼只是重置了 ViewController 的樣式,保證頁面正常顯示。一些直接新增到 Window 上檢視仍然可能出現顯示異常,在此同樣可以選擇 hook View 的相關方法,重置檢視為淺色樣式。Method swizzling 雖然功能強大,但還是儘可能選擇慎用。經過權衡我們選擇在工程是全域性搜尋新增到 window 上的相關程式碼,未能適配深色模式的檢視,手動設定為淺色樣式。

Ø 為未適配深色模式頁面新增蒙層

在保證未適配深色模式的頁面和檢視能夠顯示正常顯示後,為優化深色與淺色頁面跳轉過程中的使用者體驗,我們為未適配深色模式的頁面添加了蒙層。

圖5 蒙層效果圖

新增蒙層需要關注三個問題:

1)如何區分當前頁面是否適配深色模式

要為未適配的頁面新增蒙層,首先要區分哪些頁面已適配深色模式,哪些頁面未適配深色模式。我們已通過方法交換將所有頁面預設重置為淺色樣式,同時適配深色模式的頁面,需要手動設定為預設樣式。因此我們可以根據控制器的 overrideUserInterfaceStyle 屬性判斷一個頁面,是否支援深色模式。

2)蒙層新增位置

在頁面上新增蒙層,有兩種方案:一是為每個控制器新增蒙層,這種方案比較靈活,便於頁面自由定製,對於一些巢狀的控制器,需要特殊處理,實現起來相對複雜;另一種方案是在 Window 上新增蒙層,該方案實現起來比較簡單,而且能夠完全覆蓋整個螢幕,體驗更佳。因此我們選擇在 Window 上新增蒙層。

3)蒙層顯示隱藏時機

蒙層顯示可以在 viewWillAppear 方法中,判斷當前頁面是否支援深色模式,如果不支援深色模式,則給當前頁面新增蒙層;在 viewDidAppear 方法中,判斷如果當前頁面支援深色模式,則隱藏蒙層。

另外還需要監聽系統顯示模式切換,當系統切換到淺色模式時,移除蒙層;若系統切換到深色模式,並且當前控制器未適配深色模式,則需要顯示蒙層。

4. 如何應對後續業務迭代成本

深色模式的適配需要修改調整大量頁面和元件的顏色,如果僅僅為了適配深色模式,將所有的 UI 顏色修改一遍,這樣耗時費力的修改工作的收益,值得我們思考。能否提供一套基礎庫,提升深色模式適配開發效率,同時減輕後續業務迭代成本,是我們設計過程中重點關注的另一個問題。

基於上述思考,我們設計了樣式庫。樣式庫不僅提供了簡單易用的 API,方便業務方快速調整 UI 樣式,適配深色模式;同時實現了 UI 樣式的統一管理,讓 UI 規範化、標準化,另外兼顧了 UI 元件標準化及跨應用平移,後續業務迭代成本及擴充套件,讓深色模式適配專案更具價值。

樣式庫整體設計如下:

Ø 簡單易用的 API

首先樣式庫,提供了一套簡潔易用的 API,為深色模式的適配奠定了基礎,提高了開發效率。在沒有樣式庫之前,一個檢視適配深色模式,調整背景色,示例程式碼如下:

view.backgroundColor = [UIColor lightColorWithHex:0xD9E2E9 lightColorAlpha:0.5 darkColorWithHex:0xFFFFFF darkColorAlpha:0.0];

基於樣式庫,修改檢視背景色時,使用語義顏色,無論是否需要設定透明度,都可以這樣寫:

view.backgroundColor = [StyleLib colorWithType:@"Primary_1"];

Ø 顏色統一管理,靈活擴充套件換膚

在未使用樣式庫之前,顏色值硬編碼於程式碼之中,修改元件顏色,需要大範圍查詢替換。樣式庫對語義顏色與實際色值做對映儲存,並對顏色實現了統一管理,讓 UI 更加標準規範。業務方不再允許使用硬編碼的色值,而是直接使用語義顏色。基於樣式庫,後續如果需要調整色值,只在樣式庫層調整即可,不再需要到處修改程式碼,減輕了後續修改成本。

樣式庫的建立同時為後期主題擴充套件提供了可行性,如果要擴充套件其他主題,只需要在樣式庫底層擴充套件主題,而業務層不需要大範圍的修改,即可實現App 靈活換膚。

Ø 一次編碼,多處執行

隨著 58 App 工廠專案的快速持續推進,UI元件的跨業務複用,甚至跨應用平移實踐場景也越來越多。通常不同的應用有著獨自的設計風格,為實現 UI 元件的跨應用平移,樣式庫設計過程中,不同的 App 分別對應獨立的主題庫。真正實現了UI 元件的一次編碼,多處執行,極大減輕了 UI 元件跨應用平移成本。

在深色模式適配專案中,我們已經實現了導航欄、通用彈窗、分享面板、Loading 框、篩選器等 UI 元件的標準化,後續我們將實現更多 UI 元件的標準化,推動業務方接入,減輕後續業務迭代及平移成本。

Ø 多套面板,一套設計

樣式庫不僅提高了開發效率,而且極大減輕了設計師的設計成本,對於多套主題,設計師只需提供一套設計。

樣式庫實現了顏色的統一管理,使 UI 更加標準規範,為深色模式的快速接入奠定了基礎,深色模式的快速實現和上線更凸顯了樣式庫的重大意義。將更多的 UI 元件統一到標準組件庫中,實現元件集中化開發,UI 元件就能夠實現跨業務,跨應用複用,極大提高業務開發效率和降低後續業務迭代及平移的成本。

適配成果展示及效能優化

1. Native 頁面

2. RN 頁面

3. Web 頁面

4. 蒙層效果

圖10 蒙層效果

5. 耗電量對比

由於OLED螢幕中每個畫素都是自主發光而非LCD由整個一塊背光面板發光,所以在顯示深色元素時畫素所消耗的電流更低,尤其在純黑顏色時畫素點可以完全關閉達到省電的效果。目前,只有iPhone X、XS、XS MAX、11 Pro、11 Pro MAX 五款蘋果手機使用的是OLED螢幕。

為此針對58同城App適配深色模式後對首頁做了對比試驗:

試驗條件:

裝置:iPhoneX 系統:iOS13.3

方法:開啟58同城首頁,設定Timer觸發首頁列表來回滾動。每掉電1% 就記錄一下耗時。

試驗結果:

橫座標:時間(分)

縱座標:單位耗電量

試驗結論:

亮色模式下28分鐘耗電約為:7.08 %

深色模式下28分鐘耗電約為:5.32 %

深色模式下,基於58同城首頁,整體耗電量相比亮色模式節約25%左右。

總結

深色模式是公司內部從設計到技術,一切從使用者體驗角度出發計劃並實施的產物。為了讓使用者在58業務場景裡在深色模式下有一致的體驗,設計和技術上從Native頁面到RN、Hybird頁面進行了全面的適配。

在實施的過程中,遇到了很多困難。比較重要的難點是58業務較多,這個適配過程需要不同的業務線設計和技術進行溝通與協作。為了解決這些問題,設計上,統一了集團不同App間設計圖上關於色值的定義與描述;同時技術上引入樣式庫,統一了App內不同業務線間對色值的使用方式,簡化了亮色和暗色下呼叫,並徹底解決了長久以來不同業務間對顏色隨意使用的局面。這樣,使得使用者在使用58App的過程中,具有一致的視覺體驗。

後續,為了進一步提升使用者體驗,會和設計一同進行元件庫的構建,元件庫構建後不但能降低各業務線對元件適配深色模式的工作量,而且能統一App內元件的風格。而且對一些細節,在深色模式下會進一步打磨。因此,深色適配是其實是一個持續的專案。相信不久的將來,會積累更多深色模式的經驗和心得分享給大家。

參考文獻

1. WWDC: Implementing Dark Mode on iOS

2. Supporting Dark Mode in Your Interface

3. Choosing a Specific Interface Style for Your iOS App

4. Switching your phone to dark mode could be a game-changer for your battery life, especially if you have one of 3 iPhones

作者簡介

賈學文,58 同城 – 基礎技術部 – iOS 技術部 高階研發工程師

蔣演,58 同城 – 基礎技術部 – iOS 技術部 架構師

528

iOS

iPhone

  • 整治雙十一購物亂象,國家再次出手!該跟這些套路說再見了
  • 詳談網際網路電商時代,從淘寶的“傳統電商”到現在的“社交電商”