百度APP iOS暗黑模式适配的完美解决方案

一 背景

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

二 皮肤主题框架的诞生 BDPAppearance

2.1 业务方使用方式

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

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

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

2.2 实现原理

先来看一下BDPAppearance设计方案的架构图

在项目初始化时,会先加载当前主题所使用的色值资源到内存中,相关控件通过 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排名靠前的开源库对比:

同时对比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等,而在多种技术形态下主题模式又是如何通信呢?可以参考下边这张图:

如图所示:

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

六 项目工程色值配置

  • 6.1 端内色值表的管理

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

每个组件内部所使用到的色号仅仅是有限个, 如果所有组件用到的色值都统一放入一个色值表中管理,显然是不合理的,不利于解耦也更不利于组件化输出;那么最优的色值管理方式是什么呢?

如上图所示,组件各自管理自己所需的色值表,在项目编译时,会通过脚本将所有组件的色值表进行色值去重后,合并成一个总的色值表存储在Themes仓库下,然后初始化主题资源时读取Themes里边总的色值表即可; 此种处理方式则达到了组件间解耦的目的;

  • 6.2 Sketch插件:ThemeMeasure

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

此插件共包含三种能力:

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

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

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

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

  • 3.熟悉的标注导出方式,所有标注均在一处

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

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

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

七 支持自动跟随系统主题变化以及动画切换

八 总结

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


https://mp.weixin.qq.com/s/QOPCCIC-PbmUtuq2XUS34g

iOS APP 图标版本化

在我们的项目开发过程中,需要频繁打包给测试人员去测试,有时候我们都不知道测试机上安装的版本是否是最新的,这样会造成很多不必要的麻烦和成本。因此我们需要将buildNumber以水印的方式打在APPIcon上,可以很直观的知道当前是哪一个版本。

发布于:8天以前  |  21次阅读  |  详细内容 »

如何实现一个HTTP请求库——axios源码阅读与分析

在前端开发过程中,我们经常会遇到需要发送异步请求的情况。而使用一个功能齐全,接口完善的HTTP请求库,能够在很大程度上减少我们的开发成本,提高我们的开发效率。

发布于:9天以前  |  23次阅读  |  详细内容 »

老司机 iOS 周报 #144 | 2021-01-14

发布于:12天以前  |  44次阅读  |  详细内容 »

快手,快影 iOS App反调试

发布于:19天以前  |  65次阅读  |  详细内容 »

优酷iOS插件化页面架构方法

随着业务不停地迭代,优酷 APP 用于分发视频资源的 UI 控件越写越多,也越来越复杂,并且同时相似相近的代码也非常多。

发布于:2月以前  |  180次阅读  |  详细内容 »

iOS中的内嵌汇编

写一篇在iOS上使用汇编的文章的想法在脑袋里面停留了很久了,但是迟迟没有动手。虽然早前在做启动耗时优化的工作中,也做过通过拦截objc_msgSend并插入汇编指令来统计方法调用耗时的工作,但也只仅此而已。刚好最近的时间项目在做安全加固,需要写更多的汇编来提高安全性(文章内汇编使用指令集为ARM64),也就有了本文

发布于:2月以前  |  182次阅读  |  详细内容 »

77.9K 的 Axios 项目有哪些值得借鉴的地方

Axios 是一个基于 Promise 的 HTTP 客户端,同时支持浏览器和 Node.js 环境。它是一个优秀的 HTTP 客户端,被广泛地应用在大量的 Web 项目中。

发布于:2月以前  |  166次阅读  |  详细内容 »

不会吧,这也行?iOS后台锁屏监听摇一摇

一般情况下,出于省电、权限、合理性等因素考虑,给人的感觉是很多奇怪的需求安卓可以实现,但是iOS就无法实现!今天要介绍的需求也有这种感觉,就是“当 APP 处于后台或锁屏状态时,依旧可以监听到摇一摇,进而触发某些功能,比如:语音播报”。

发布于:3月以前  |  306次阅读  |  详细内容 »

iOS 稳定性:App 被终止的原因

本次 session 主要内容如下: 介绍了后台应用终止的常见原因,并提供了一些优化建议 介绍了 MetricsKit 提供的在代码中获取诊断和性能数据的方法 介绍了 Xcode Metrics Ogranizer 提供的关于线上用户性能数据的可视化报告

发布于:3月以前  |  421次阅读  |  详细内容 »

优酷iOS插件化页面架构方法

随着业务不停地迭代,优酷 APP 用于分发视频资源的 UI 控件越写越多,也越来越复杂,并且同时相似相近的代码也非常多。

发布于:3月以前  |  318次阅读  |  详细内容 »

Vue中Axios的封装和API接口的管理

在vue项目中,和后台交互获取数据这块,我们通常使用的是axios库,它是基于promise的http库,可运行在浏览器端和node.js中。他有很多优秀的特性,例如拦截请求和响应、取消请求、转换json、客户端防御XSRF等。所以我们的尤大大也是果断放弃了对其官方库vue-resource的维护,直接推荐我们使用axios库。如果还对axios不了解的,可以移步axios文档。

发布于:3月以前  |  292次阅读  |  详细内容 »

iOS 持续集成:更完备的 App Store Connect API

时隔两年 App Store Connect API 有了更新,WWDC 2018 推出了 App Store Connect API ,用于自动化一些 App Store Connect 后台操作。这次更新包含了 app 元数据相关的API,补上了原来缺失的重要一环, 使得几乎可以通过 App Store Connect API 完成 App Store Connect 上的所有操作。今后开发、证书配置、用户管理、测试、发布全流程都可以通过 API 完成。

发布于:3月以前  |  354次阅读  |  详细内容 »

iOS 性能优化:优化 App 启动速度

苹果是一家特别注重用户体验的公司,过去几年一直在优化 App 的启动时间,特别是去年的 WWDC 2019 keynote[1] 上提到,在过去一年苹果开发团队对启动时间提升了 200%

发布于:3月以前  |  327次阅读  |  详细内容 »

iOS圆角的离屏渲染,你真的弄明白了吗

发布于:3月以前  |  307次阅读  |  详细内容 »

iOS导航栏整体滑动解决方案(类似淘宝)

发布于:3月以前  |  359次阅读  |  详细内容 »

让你的应用远离越狱:iOS 14 App Attest 防护功能

当越狱在 iOS 设备第一次流行起来时,iOS 开发人员会尝试各种方法来保护自己的应用程序,以让应用免受盗版等不确定因素的困扰。有许多方法可以做到这一点,包括检查 Cydia 是否存在、检测应用程序是否可读取自身沙箱之外的文件、在检测到调试器时让应用程序崩溃等等。

发布于:3月以前  |  346次阅读  |  详细内容 »

探秘 iOS 14 的 WidgetKit

Widget Extension 提供了 small, medium, large 三个尺寸,不同尺寸可以展示不同的数据、不同的界面,开发者也可以锁定自己APP的 Widget 只有某类尺寸,相同的widget也能重复添加。作为添加在主屏幕上的控件,苹果用了 “At a glance” 来形容 widget ,所以 widget extension 是无法交互的,它能做的只有展示一些信息与点击两个作用,点击后就会引导至app,同时为了性能与耗电量的考虑,Widget extension 也不能展示视频和动态图像。

发布于:3月以前  |  383次阅读  |  详细内容 »

iOS 的自动构建流程

发布于:3月以前  |  410次阅读  |  详细内容 »

iOS14 Widget 万字指北,先人一步获得顶级流量

2020 年 6 月 22 日,苹果召开了第一次线上的开发者大会 - WWDC20。这次发布会上宣布了ARM架构Mac芯片(拳打Intel)、iOS 14 ATT(脚踢Facebook),可谓是一次载入史册(我是爸爸)的发布会了,当然还发布了被称为下一个顶级流量入口的Widget。踩着八月的尾巴,本次我们就来探究一下Widget。本文会从Widget初窥和Widget开发两个维度和章节来探究一下Widget, 其中初窥章节会带您简单的了解一下Widget,适合应用决策者阅读; 开发章节会带着您一步一步的完成设计开发Widget,适合程序员阅读。

发布于:3月以前  |  523次阅读  |  详细内容 »

iOS 性能优化 - Allocations分析内存分配

发布于:3月以前  |  375次阅读  |  详细内容 »

最多阅读

快速配置 Sign In with Apple 1年以前  |  3784次阅读
开篇 关于iOS越狱开发 1年以前  |  2565次阅读
APP适配iOS11 1年以前  |  2526次阅读
使用 GPUImage 实现一个简单相机 1年以前  |  2509次阅读
给数组NSMutableArray排序 1年以前  |  2487次阅读
在越狱的iPhone设置上使用lldb调试 1年以前  |  2440次阅读
App Store 审核指南[2017年最新版本] 1年以前  |  2336次阅读
UITableViewCell高亮效果实现 1年以前  |  2318次阅读
所有iPhone设备尺寸汇总 1年以前  |  2258次阅读
使用ssh访问越狱iPhone的两种方式 1年以前  |  2193次阅读
关于Xcode不能打印崩溃日志 1年以前  |  2117次阅读
使用ssh 访问越狱iPhone的两种方式 1年以前  |  2016次阅读
UIDevice的简单使用 1年以前  |  1805次阅读
为对象添加一个释放时触发的block 1年以前  |  1783次阅读
使用最高权限操作iPhone手机 1年以前  |  1749次阅读