ROMA-iOS适配深色模式总结

一、背景

深色模式在低光环境下(如夜间使用)可以显著减少屏幕发出的蓝光,降低眼睛疲劳,减轻视觉压力。深色背景配合浅色文字能提供更好的对比度和可读性,减少眩光,让内容更易于阅读。深色模式还可以显著节省电量,延长设备的电池续航时间。随着深色模式的普及,越来越多的用户习惯并偏好深色界面。

京东金融App自8.0.20版本支持深色模式,可在设置->通用->深色模式中打开深色模式的设置页面,选择是否启用深色模式。ROMA框架也在此版本全面支持深色模式的设置,本文详细介绍跨端框架ROMA适配深色模式的过程。

二、ROMA配置和使用

APP深色模式的设置,提供如下选择,可以强制设置普通模式(浅色模式)、深色模式和跟随系统。可以看到在将App切换为深色模式时,页面视图由普通模式切换深色模式时原有视图并未重建,视图模式的转换也非常顺畅,体验很好。







1.视图的显示模式设置

ROMA针对所有标签(包括页面)提供 theme-mode 属性,表示当前视图在什么模式下显示。提供以下三种模式可选:

1:表示强制浅色模式 2:表示强制深色模式 3:表示跟随模式(节点设置跟随,表示跟随父节点。页面设置跟随,表示跟随系统。若未设置:页面为1,标签3)

如下示例代码,设置当前页面 theme-mode=3 表示跟随window(window设置跟随系统),div标签设置 theme-mode="1" 表示强制浅色,text标签未设置,则默认为 theme-mode="3",跟随父节点div,因次text标签也是浅色。

css 复制代码
<template theme-mode=3>
    <div style="align-self: stretch; height: 100px; margin: 10px;" theme-mode="1">
          <text style="color:#000000; font-size: 12px;"> 文本测试 </text>
    </div>
</template> 

通过设置 theme-mode 属性,业务可以灵活的定制页面和视图的显示模式。既可实现整体视图跟随父节点的模式,也可针对特殊节点强制深色或者浅色显示,更大程度的满足业务在不同场景下的需求,具体效果可参考下图,其中数字表示当前节点对应的 theme-mode 的值。





2.视图的颜色设置

理想情况下业务可以做到零修改就能完成深色模式的适配,前提是App需要高度依赖一套完整的、规范的颜色映射表(普通模式下的颜色color要有对应的深色模式下的颜色值color-dark,没有则不映射),且设计师要按照映射表上的颜色来设计UI设计稿。这样在模式切换时就可以自动调整视图的颜色。

配置表的颜色映射流程可参考下图:





但业务场景复杂多样,为了兼容更多的业务场景,ROMA也向所有视图提供了针对深色模式的特定样式配置 xxx-dark ,来定制深色模式下的UI样式。如下示例代码,提供了 background-color-dark 字段可以配置深色模式下视图的背景色,color-dark 字段设置深色模式下文本的颜色, src-dark 字段设置深色模式下图片的资源。

css 复制代码
 <div style="background-color: white; background-color-dark: '#EF4034';">
      <text style="color: '#666666'; color-dark:'#F9F9F9';"> 文本测试 </text>
      <img src="https://img0.baidu.com/it/u=3838093562,4126749835&fm=253&fmt=auto&app=138&f=JPEG?w=1144&h=500" 
           src-dark="https://imgs.699pic.com/images/500/465/562.jpg!list1x.v2">
</div>

三、视图模式切换原理分析

ROMA 只提供了简单配置就可以让业务适配视图在深色模式的显示,极大简化了业务的适配工作,其实App切换深色模式的处理流程涉及视图层级的方方面面,下面以iOS端为例,着重从视图层级的变化上介绍模式切换所触发的整个流程。

1.UITraitEnvironment 协议详解

UITraitEnvironment 是 iOS 中一个基础协议,各类视图和控制器都已实现了这个协议,用于监听和处理界面环境特征的变化。它是 iOS 自适应布局和外观系统的核心组成部分,使应用能够响应各种环境变化,如深浅模式切换、设备旋转或者尺寸类别变化。

less 复制代码
@protocol UITraitEnvironment <NSObject>
@property (nonatomic, readonly) UITraitCollection *traitCollection API_AVAILABLE(ios(8.0));
/*! To be overridden as needed to provide custom behavior when the environment's traits change. */
- (void)traitCollectionDidChange:(nullable UITraitCollection *)previousTraitCollection API_DEPRECATED("Use the trait change registration APIs declared in the UITraitChangeObservable protocol", ios(8.0, 17.0), visionos(1.0, 1.0)) API_UNAVAILABLE(watchos);
@end

在iOS17之后推荐使用 UITraitChangeObservable 协议,可更精细定制要监控的特征值,并将新旧特征值都封装到hander中处理,使用起来更方便。

swift 复制代码
API_AVAILABLE(ios(17.0), tvos(17.0)) API_UNAVAILABLE(watchos) NS_SWIFT_UI_ACTOR
@protocol UITraitChangeObservable
- (id<UITraitChangeRegistration>)registerForTraitChanges:(NSArray<UITrait> *)traits withHandler:(UITraitChangeHandler)handler;
- (id<UITraitChangeRegistration>)registerForTraitChanges:(NSArray<UITrait> *)traits withTarget:(id)target action:(SEL)action;
- (id<UITraitChangeRegistration>)registerForTraitChanges:(NSArray<UITrait> *)traits withAction:(SEL)action;
- (void)unregisterForTraitChanges:(id<UITraitChangeRegistration>)registration;
@end

2.系统特征值改变对各层级视图的影响

iOS的核心类 UIScreen、UIWindow、UIViewController、UIView 等都实现了 UITraitEnvironment 协议,特征集合通过视图层次结构自上而下传递:

1.系统级特征由 UIScreen 提供

2.UIWindow 从 UIScreen 继承特征

3.根视图控制器从 UIWindow 继承特征

4.子视图控制器从父视图控制器继承特征

5.视图从其视图控制器继承特征

6.子视图从父视图继承特征

7.layer层监听对应视图层的特征来调整自身的特征

以下展示系统显示模式由浅色切换为深色时各层级视图特征值的变化过程。





3.视图的颜色设置

如果是视图的直接颜色属性,比如文本颜色textColor,视图的背景色backgroundColor等,可以通过DynamicColors 直接设置,这样在视图模式发生变化的时候,会自动获取对应模式的颜色值。

objectivec 复制代码
UIColor* dycolor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traitCollection) {
       if ([traitCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {
           return  lightResoledColor;
       } else {
           return darkResoledColor ? darkResoledColor : (lightResoledColor ? lightResoledColor : [UIColor clearColor]);
       }
}];
view.backgroundColor = dycolor;

4.视图的其他设置

对于layer层颜色、图片资源变化、lottie的资源切换等,可以向view层注册状态变更的事件回调,在视图的显示特征的发生变化时,调整视图的显示。如下示例在监测到view的模式发生变化时,调整图层的边框颜色。

ini 复制代码
UIColor* borderColor = jr_themeColorForTrans(_bordercolor_light, _bordercolor_dark); 
CAShapeLayer* borderLayer = [[CAShapeLayer alloc]init];
__weak CAShapeLayer* __weakBorderLayer = borderLayer;
[self.view jr_registerForJRThemeChangesWithHandler:^(JRThemeModeStyle style) {
       [CATransaction begin];
       [CATransaction setDisableActions:YES];
       __weakBorderLayer.strokeColor = borderColor.CGColor;
       [CATransaction commit];
 } key:@"jr_trans_layer_border_color_key"];

四、总结

ROMA作为一个现代化的跨端框架,深色模式的适配不仅是一项功能,更是我们对用户体验、设备兼容性和技术趋势的全面考量。我们相信,这一更新将为开发者提供更大的灵活性,并最终为用户带来更加优质的产品体验。同时,在深色模式开发的过程中,我们也梳理出多主题切换的实现方案,如有需要,可快速完成多主题的适配。

相关推荐
demo007x3 小时前
让大模型活在你的鼠标旁:我用 Tauri 2 + Rust 打造了一款“反直觉”的 AI 全局划词效率神器
macos·程序员·llm
程序员海军5 小时前
AI 越来越强,为什么打工人反而越来越累、越来越内耗了?
前端·程序员·aigc
badhope9 小时前
同样写 AI,为什么有的文章 549 人看、有的只有 16 人看?——我用 40 篇掘金文章的真实数据复盘
程序员
今朝唯我少年郎1 天前
Codex安全盲区代码漏洞生成实测
python·程序员
kyriewen2 天前
我用 AI 写完一个需求后才发现,最难的不是 prompt,而是验收
前端·程序员·ai编程
酷酷的逗逗乐2 天前
前端转 Agent 开发 · 第六节
前端·程序员
爱勇宝2 天前
没有 Fn 键关触控板?我做了一个双击即用的 Windows 小工具
前端·后端·程序员
程序员cxuan2 天前
跟 WebUI 说再见了,最强 DeepSeek 桌面端来了!
人工智能·后端·程序员
codigger2 天前
用 AI 写代码,你知道它会把我的代码传去哪?
ai·程序员·编程·数据安全
程序员cxuan2 天前
DeepSeek V4.1 Flash 正式发布!
人工智能·后端·程序员