Categories
程式開發

優酷暗黑模式(十):消費場景落地(iOS)


一、概述

iOS 13 中蘋果引入了暗黑模式,提供了全新的配色方案,非常適合暗光環境下使用,對眼睛的傷害更小。為了配合優酷App全站暗黑模式的適配,我們需要在優酷主客消費場景將暗黑模式全面落地。

值得慶幸的是,在這之前優酷主客消費場景已經完成了統一架構遷移和性能優化,並在這個過程中完成了大部分插件的治理,遵循統一架構、組件標準化、DesignToken設計,使得消費場景可以快速高效地支持暗黑模式。

二、業務介紹

優酷主客消費場景即優酷App的播放頁。播放頁作為視頻內容消費的落地頁,主要提供視頻播放、視頻內容介紹、互動、視頻推薦、視頻花絮、視頻周邊等內容推薦,業務場景及頁面內容都比較複雜。根據場景分為:劇集、電影、綜藝、少兒、體育、新知等;其內容有:組件、半屏、Tab等。

組件:即視頻相關內容承載控件,包括簡介,選集,周邊視頻,花絮視頻,推薦視頻等,通過這些內容,讓用戶了解更多的視頻相關的信息。

半屏:包括Native、Weex、H5的半屏,通過半屏用戶可以看到更多的視頻相關內容,也可以承載視頻互動。因為組件展示的內容還是有限,通過半屏可以更好更全地展示。

Tab:通過Tab讓用戶在不同的內容之間切換。

播放頁架構圖:

1)頁面、組件、坑位結構圖:

優酷暗黑模式(十):消費場景落地(iOS) 1

2)業務效果圖:

優酷暗黑模式(十):消費場景落地(iOS) 2

三、適配策略

優酷暗黑模式(十):消費場景落地(iOS) 3

1、頁面適配

頁面展現主要分為幾種狀態:加載態(Loading),展現態,異常態。主要工作可以分為加載態LoadingView適配,展現態背景色適配,異常態ErrorView適配。

按道理來說,背景色適配工作量會非常大,因為需要一層一層地去設置子 View 的背景色。但是由於優酷的視圖中,大部分背景色是透明的,使得在進行暗黑模式適配的過程中非常便利,不用去一層一層的設置背景色,只需要適配容器View就可以。

[self.containerView setBackgroundColor:UIColor.ykn_primaryBackground]; //设置背景色,ykn_primaryBackground 为我们暗黑模式框架对外暴露的属性通过它可以取到对应的颜色
[self.view setBackgroundColor:UIColor.ykn_primaryBackground]; //设置背景色

2、組件適配、坑位適配

因為播放頁都已經拆分為組件和坑位,所以完成頁面部分的適配之後,主要就是對組件和坑位的適配。由於播放頁組件完成度很高,所以在進行組件適配的時候也是比較順利的。舉個例子,播放頁很多的組件都有 Header View,即左面會有文字,右面有一個箭頭的 image。

優酷暗黑模式(十):消費場景落地(iOS) 4

當替換完Header View 組件的圖片,並進行完文字的暗黑模式適配後,所有這些UI 佈局全部完成了適配工作,從這裡可以看出組件標準化的完成程度對後期業務的維護有著深遠的影響。

_titleLabel.textColor = UIColor.ykn_primaryInfo;   //设置组件头部标题颜色
_subtitleLabel.textColor = UIColor.ykn_secondaryInfo;  //设置组件头部副标题颜色
_arrowImageView.image = UIImage.ykn_detail_comp_header_more;   //设置组件头部箭头图片

3、半屏適配

半屏頁面主要是分為Native半屏頁面、H5半屏頁面、Weex半屏頁面。

由於所有的半屏頁面都是由播放頁半屏容器管理器來管理,所以只需要在裡面統一適配即可;需要注意的是我們適​​配的只是H5、Weex的容器,容器裡面的具體頁面的適配詳見: 暗黑模式的技術支撐(Weex&H5)。

舉例來說,H5半屏容器頁面適配:

webViewController.view.backgroundColor = UIColor.ykn_primaryBackground;  //设置容器背景色
webViewController.topBar.backgroundColor = UIColor.ykn_primaryBackground; //设置顶部导航背景色
webViewController.titleLabel.textColor = UIColor.ykn_primaryInfo;  //设置顶部导航字体颜色
[webViewController.closeBtn setImage:UIImage.ykn_detail_box_close forState:UIControlStateNormal];//设置顶部导航关闭按钮图片

4、中間件處理,與沈浸式隔​​離

在適配暗黑模式之前,優酷播放頁已經完成了沉浸式模式的開發。沉浸式使得用戶可以快速地融入到內容本身中去,減少不必要的信息造成的視覺干擾,使用更有層次感的內容來引導用戶聚焦,自然地實現播放器視覺C位呈現,其呈現效果與暗黑模式也有較大不同。

所以,在適配的過程中需要做一層顏色及資源管理層(架構圖中的顏色資源管理層),來區分沉浸式氛圍及暗黑模式。通過不同的開關控制背景處理及加載不同的資源、顏色。後期沉浸式模式將會接入優酷App的全局統一氛圍配置,做到自動下發氛圍相關樣式,減少適配工作。

優酷暗黑模式(十):消費場景落地(iOS) 5

在進行與沈浸式隔​​離開發的時候我們遇到一個難點,在沒有進行暗黑模式適配時候,沉浸式是通過函數

-(NSString *)immersionImageName:(NSString *)imgName

進行適配,該函數實現邏輯如下:

-(NSString *)immersionImageName:(NSString *)imgName
{
   if (self.isImmersionMode) {
       NSString *name = [self.imgNameDic valueForKey:imgName] ? : imgName;
       return name;
   } else {
       return imgName;
   }
}

該函數首先會先判斷是否為沈浸式模式,如果為沈浸式返回對應沉浸式的圖片名稱; 當找不到對應圖片時,使用一個兜底圖片。如果沉浸式沒有生效則直接返回正常圖片名稱。

當適配暗黑模式時候遇到了難點,優酷設計標準化SDK是通過屬性調用返回 UIImage 對象,所以需要改造該函數。

先貼上改造後結果:

-(UIImage *)immersionImage:(NSString *)imgName
{
   if (self.isImmersionMode) {
       if ([self.imgNameDic stringForKey:imgName]) {
           return [UIImage imageNamed:[self.imgNameDic stringForKey:imgName]];
       }
   }
   SEL sel = NSSelectorFromString(imgName);
   if ([UIImage respondsToSelector:sel]) {
       return [UIImage performSelector:sel];
   } else {
       return nil;
   }
}

首先將函數返回結果改為 UIImage 對象。這麼改造有兩點好處:

第一: 在業務方調用時,可以直接使用該函數返回的結果,不需要再調用函數

(nullable UIImage *)imageNamed:(NSString *)name

便於業務方使用。

第二: 如果當前為暗黑模式,可以直接返回該屬性值,不需要進行轉換等操作,提高運行效率、降低出錯概率。

最後: 因為傳進來的是圖片名稱,並且屬性名稱是與圖片名稱一致的,所以可以通過其屬性 get 方法拿到對應圖片。

總結一下該函數的邏輯:如果為沈浸式模式,返回對應的沉浸式 UIImage 對象 ,因為沈浸式業務優先級大於暗黑模式。如果不是沉浸式,通過 NSSelectorFromString 生成該屬性 get 方法對應的 selector,判斷是否存在該方法。如果存在該方法說明存在對應圖片,調用該屬性 get 方法並返回對應 UIImage 對象。

5、網絡圖片的適配

暗黑SDK 是直接支持本地圖片適配的,但是網絡圖片適配需要業務方來做。在播放頁,有些圖片是支持後台配置的。即當後台配置了圖片下發,優先展示後台配置的圖片,如果後台沒有配置圖片則展示本地圖片。

例如下圖中的熱評、收藏、緩存、分享對應圖片都是支持後台配置的。

優酷暗黑模式(十):消費場景落地(iOS) 6

那麼問題來了,這種情況下如何進行暗黑模式的適配?

解決方案為提前準備好從網絡下載的圖片,對應的圖片有兩套即正常模式和暗黑模式。使用時候判斷對應模式來使用對應圖片。

示例代碼如下:

UIImage *imageFromWeb1 ;//从网络下载成功的UIImage
UIImage *imageFromWeb2 ;//从网络下载成功的UIImage
UIImage *image = [UIImage ykn_imageWithThemeProvider:^UIImage * _Nonnull(__kindof YKNThemeManager * _Nonnull manager, NSString * _Nullable identifier, NSObject * _Nullable theme) {
             //回调中不要做耗时操作,如是网络图,提前准备好图片
             if ([identifier isEqualToString:YKNThemeIdentifierDark]) {
                 return imageFromWeb1 ;  //dark模式下的图
             }
             return imageFromWeb2; //light模式下的图
         }];

四、適配效果

優酷暗黑模式(十):消費場景落地(iOS) 7

優酷暗黑模式(十):消費場景落地(iOS) 8

五、調試小技巧

在進行暗黑模式適配時候遇到這樣一個問題,即每次改過 UI 我們都需要重新運行整個App才會生效。毫無疑問,這是十分浪費時間的,有沒有什麼方案可以做到不重新運行 App 即可生效的呢?答案是有的。

在模擬器上我們可以通過 InjectionIII 來進行熱更新,具體方案不再贅述,網絡上有很多介紹文檔。這裡只介紹真機實現方案。

實現方案:首先添加一個斷點,接下來編輯斷點並添加一個 Debugger Command並輸入表達式,最後選擇 Automatically continue after evaluating actions。為什麼要勾選這個呢?如果勾選上運行到該斷點的時候不會停住,會自動執行表達式,並自動執行下一行代碼。

優酷暗黑模式(十):消費場景落地(iOS) 9

六、項目總結

業務架構層面核心功能組件化的好處是巨大的,這使得代碼按照功能模塊高度聚合,只需要針對當前功能組件進行修改即可全局生效;UI層級的扁平化及層級管理也是十分重要的。

通過暗黑模式的適配,大大的提升了優酷播放頁的使用體驗。這也是我們對iOS 13新特性的一個探索,並且在較短時間內拿出來一個成熟的暗黑模式適配方案,在優酷iOS 端進行了嘗試和使用,最終呈現的效果也是非常好的,通過這次的改造和實踐也為我們以後對其它新技術的探索打了堅實的基礎。

作者簡介

子荀、金籽,阿里文娛無線高級開發工程師。

相關閱讀

優酷暗黑模式(一):是什麼、為什麼、如何落地?

優酷暗黑模式(二):如何建立設計語言標準化管理體系

優酷暗黑模式(三):暗黑模式設計指南

優酷暗黑模式(四):設計標準化的技術實現

優酷暗黑模式(五):暗黑模式的技術實現策略

優酷暗黑模式(六):暗黑模式的技術支撐 iOS

優酷暗黑模式(七):暗黑模式的技術支撐 Weex & H5

優酷暗黑模式(八):分發場景落地(Android & iOS)

優酷暗黑模式(九):消費場景落地(Android)