16

一 背景

在2019WWDC的开场演讲中,苹果公布了即将推出的iOS13 DarkMode的新特性。此新特性不仅可以在夜晚保护视力,而且对于使用OLED的最新一代设备而言,也可以帮助用户节省电量消耗。不过此特性只支持iOS13以上的系统,为了给全系统所有用户最好的体验,研发出了一套皮肤主题框架,不仅可以全系统支持DarkMode,还可以扩展多套皮肤主题;

二 皮肤主题框架的诞生 BDPAppearance

适配前后.jpg

2.1 业务方使用方式

  • 目前系统所有控件及其Color属性和Image属性均已支持, 此处只列举两个例子:
// UIColor
view.backgroundColor = BDPAppearanceColor(@"C1");

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

业务方只需如上使用简单的API设置Color和Image,即可实现主题换肤;

2.2 实现原理

先来看一下BDPAppearance设计方案的架构图
架构图.jpg
在项目初始化时,会先加载当前主题所使用的色值资源到内存中,相关控件通过 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排名靠前的开源库对比:
开源库对比.jpg
同时对比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等,而在多种技术形态下主题模式又是如何通信呢?可以参考下边这张图:
主题模式通信.jpg
如图所示:

  • 初始化WKWebView时采用的方案:在UserAgent中拼接Key-Value的方式初始化WebView,达到在渲染时,最早时机拿到主题模式;
  • 主题变化通信采用的方案:数据通道和端能力,其本质是JS交互;

六 项目工程色值配置

  • 6.1 端内色值表的管理

整个百度App涉及近百个业务,组件近三百个,色号表的管理也显得尤为重要;

每个组件内部所使用到的色号仅仅是有限个,如果所有组件用到的色值都统一放入一个色值表中管理,显然是不合理的,不利于解耦也更不利于组件化输出;那么最优的色值管理方式是什么呢?
色值管理_1.jpg
如上图所示,组件各自管理自己所需的色值表,在项目编译时,会通过脚本将所有组件的色值表进行色值去重后,合并成一个总的色值表存储在Themes仓库下,然后初始化主题资源时读取Themes里边总的色值表即可;此种处理方式则达到了组件间解耦的目的。

  • 6.2 Sketch插件:ThemeMeasure

早期的开发中,UE出图都是用的Sketch导出HTML格式标注图,而根据百度App iOS相关 CRD、FE 对实现技术的选型及配合要求,UE 需要提供并维护一套 NA+H5 色表,标注界面时标颜色的编号而非色值。为了达到此种效果,我们同期研发出了Sketch插件,可以在标注界面直接显示出色号,解决了UE标注色号的痛点,大大提高了效率;如下图:
sketch.jpg

此插件共包含三种能力:

  • 1.多种便捷方式助力色号标注

    Theme Measure 可以让设计师根据系统的推荐选择色号进行标注,还有贴心的批量标注向导,改变了过去设计师手动写色号进行标注的方式

  • 2.一键转换深色、夜间等主题

    Theme Measure 能够让设计师一键将默认主题转换为深色、夜间或者其他主题(需要有相应的色值数据,正确的色号标注),改变了过去设计师需要手动逐一调整产出深色、夜间设计稿的方式,大幅提升设计师对多主题适配的工作效率及体验

  • 3.熟悉的标注导出方式,所有标注均在一处
    还是使用熟悉的工具导出标注,色号、布局、字号等标注均在同一个 HTML 文档内,改变了过去需要额外提供一份色号标注的方式,提升设计与研发的协同效率与体验

在整套皮肤主题机制下业务方仅仅花了不到两周的时间即完成了整个手百的主题适配,也从侧面证实此框架的优点:轻量级,使用成本低

资源配置同时也支持云端下发,可动态新增多种主题;

七 总结

本文主要从皮肤主题框架实现、色值表的管理以及配套工具链等方面详细的介绍了百度App iOS暗黑模式的适配,欢迎业内的朋友一起交流学习;


本文作者:
liushaohua


在微信-搜索页面中输入“百度App技术”,即可关注微信官方账号;或使用微信识别以下二维码,亦可关注。
微信链接.jpg


百度App技术
412 声望1.2k 粉丝