在iOS 13 中,苹果推出了深色模式。这提供了更好的夜视保护并节省应用程序功耗。不过,苹果提供的深色模式仅支持iOS 13。我们希望能够在iOS 13 之前的系统上支持深色模式,为用户提供更好的体验。它还赋予用户手动关闭应用程序的黑暗模式并选择不遵循系统主题更改的权利。
京东App包含的业务模块较多,整体适配工作量巨大。为了解决上述问题,并通过统一的接口提供对各个模块的快速访问,我们开发了Diablo基础组件,提供以下功能:
支持iOS 9及以上系统,同时也兼容iOS 13系统。深色模式支持以下系统模式,在没有内置调试工具帮助的情况下也可以使用:开发人员可以支持颜色模式扩展,以快速调试并提高效率。
基本的组件设计方案是:
业务访问
当你访问你的业务时,你需要调用基础组件提供的jdbAppearance_bindUpdater 方法,并向其传递一个Block 来处理你的UI 更新逻辑。基本组件将Block 绑定到UIView 并将UIView 存储在HashTable 中。遍历HashTable 并执行绑定块以在适当的时间更新UI。业务组件的访问方案如下:
请注意,遍历HashTable 时并非所有块都会执行。如果窗口有值,则执行绑定到UIView 的块。如果标记为稍后运行,则该块将在下次UIView 显示在窗口中时运行(调用didMoveToWindow 时)。
此外,该块被标记为仅在颜色模式更改时稍后运行,因此您不必担心每次didMoveToWindow 时都会调用该块。
当涉及到接口调用等异步场景时,访问成本是否会增加?通过下面的代码示例看看您的业务是如何适配的?
cell.viewA.backgroundColor=[UIColor redColor];cell.viewB.image=[UIImage imageNamed:@\’xxx\’];//访问后@weakify(cell)[cell jdbAppearance_bindUpdater:^(JDBAppearance *apperance, UIView *bindView) { @ Strongify(cell) cell.viewA.backgroundColor=[UIColor jdbAppearance_colorBR]; cell.viewB.image=[UIImage jdbAppearance_imageNamed:@[@\’light_xx\’, @\’dark_xx\’]];}];每次调用jdbAppearance_bindUpdater 时,块因为即时运行,访问统一,无论是否涉及异步场景,都没有额外的访问成本。
自定义更新程序:
虽然Block机制基本涵盖了所有的适配场景,但在实际开发中你可能需要一些便捷的方法,比如直接调用jd_setBackgroundColor方法来设置UIView的背景色。
我们也可以满足这些需求。我们来看看如何封装这样的API。
@implementation UIView (CustomUpdater)- (void)jdb_setBackgroundColor:(NSArray *)colors{ [self jdbAppearance_bindUpdater:^(JDBAppearance * _Nonnull 外观, UIView * _Nonnull bindingView) {bindView.backgroundColor=[UIColor jdbAppearance_colorWithHex:color s] } updaterKey:@ \’j db_setBackgroundColor\’]; updaterKey允许单个UIView绑定多个Block。它也非常易于使用,您不必担心循环引用问题。
[cell jdb_setBackgroundColor:@[@\’#FFFFFF\’, @\’#1D1B1B\’]];在应用程序中切换到深色模式
该功能允许用户在应用程序内手动打开或关闭深色模式,但存在以下问题:
如果系统中开启了深色,但应用程序中关闭了深色,则某些系统控件(例如通过UIImagePickerController 调用的系统相册)的颜色仍然会保持深色,并且系统控件的颜色会出现差异颜色和实际颜色之间。该应用程序的。
在深入研究解决方案之前,我们先介绍一下UITraitCollection。
UITraitCollection 是从iOS 8 开始添加的新类。管理与应用程序用户界面相关的多个系统功能。每个视图都有自己的UITraitCollection。
有关iOS 13 中颜色模式的信息存储在userInterfaceStyle 属性中。如果要单独指定视图的userInterfaceStyle,则必须使用新的iOS 13 API overrideUserInterfaceStyle。此外, overrideUserInterfaceStyle 设置对子视图生效。
但是视图太多了,你需要改变谁的属性呢?下图展示了视图和UITraitCollection 的传递路由之间的层次关系。
UITraitCollection 是从上到下传递的,但是UIScreen 和UIWindowScene 没有提供overrideUserInterfaceStyle API。你只能通过改变UIWindow的属性来让UIWindow及其所有子视图显示你设置的颜色。
如果启用黑暗,请将所有窗口的overrideUserInterfaceStyle 设置为UIUserInterfaceStyleDark。如果关闭黑暗,请将所有窗口的overrideUserInterfaceStyle 设置为UIUserInterfaceStyleLight。如果UIWindowDid注册了becomeVisibleNotification通知后出现新窗口,应该如何处理这种情况?当显示UIWindow 对象时,此通知设置窗口的overrideUserInterfaceStyle 属性。
摘要: 通过更改窗口的overrideUserInterfaceStyle 属性,您可以将大多数系统控件的颜色与应用程序的颜色相匹配。
监控系统模式切换
为什么要提这个?仅仅使用TraitCollectionDidChange 来监控它还不够吗?
因为更改overrideUserInterfaceStyle 后我发现切换系统颜色模式时,窗口及其子视图上的TraitCollectionDidChange 没有被调用。
虽然官方文档中没有明确的说明,但可以确认的是,只要窗口的overrideUserInterfaceStyle 设置为UIUserInterfaceStyleDark 或UIUserInterfaceStyleLight ,就无法观察到窗口及其子视图。仅启用默认的UIUserInterfaceStyleUnspecified。
那么我们应该做什么呢?更改了所有窗口的overrideUserInterfaceStyle。
解决办法总是比困难多。仔细分析一下,改变窗口的overrideUserInterfaceStyle来同步改变系统控件的颜色。然后我可以创建一个单独的ObserveWindow 并在切换模式时跳过它(如果它是ObserveWindow)并且仅更改另一个窗口的overrideUserInterfaceStyle 吗?这样,您可以在ObserveWindow 上实现TraitCollectionDidChange 方法来处理监视系统模式切换和更新应用程序UI 的逻辑。
@implementatiton ObserveWindow- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection{ if (@available(iOS 13.0, *)) { if ([self.traitCollection hasDifferentColorAppearance CompareToTraitCollection:previousTraitCollection]) { //1.更改应用的内部样式//2 改变其他事情。 window\’s overrideUserInterfaceStyle //3.通知商家更新UI } }}@end 多任务界面快照
在适应过程中,我们发现了一个问题。在多任务界面中,应用程序显示的颜色与系统颜色模式完全相反。
进一步分析发现,应用进入后台时,traitCollectionDidChange会执行两次,系统userInterfaceStyle分别为UIUserInterfaceStyleDark和UIUserInterfaceStyleLight。
为什么这个?当traitCollectionDidChange被调用时我查看了堆栈。
如果您查看堆栈,您会发现系统在进入后台时会拍摄快照。这个快照实际上是出现在系统多任务界面中的快照,被调用来分别拍摄暗快照和亮快照。当您进入多任务界面时,系统会根据当前的颜色模式显示正确的快照。
为什么会出现反色模式问题呢?这里先介绍一下“跟随系统”的功能。
该应用程序有一个开关来控制它是否遵循系统颜色模式。当用户第一次选择切换到深色模式时,根据系统的说法,此时应用程序模式将与系统模式匹配。如果关闭“跟随系统”开关,将不再监控系统模式切换,并优先选择用户在应用中选择的模式。
如果关闭跟随系统开关,应用程序的颜色模式可能与系统颜色模式不匹配。例如,深色模式图像会遮挡浅色模式图像。为了避免这个错误,我们添加了一个条件,即只有“跟随系统”打开时才启用快照功能。
修改后的traitCollectionDidChange实现如下:
-(void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection{ if (@available(iOS 13.0, *)) { UIApplicationState state=[UIApplicationsharedApplication].applicationState if (state==UIApplicationStateBackground) { //当系统切换到后台时,Invert颜色模式并拍摄两张照片JDBAppearanceManager *manager=[JDBAppearanceManagersharedInstance]; if (manager.followSystemMode) { //根据系统更新UI,更新UI后系统会拍摄快照。 Completed} } else { //触发场景:系统控制中心切换模式、后台转到前台、Xcode调试菜单切换模式if ([self.traitCollection hasDifferentColorAppearanceCompareToTraitCollection:previousTraitCollection]) { //1. 更改应用内部样式//2.更改其他窗口overrideUserInterfaceStyle //3. 通知业务更新UI } } }}个性化
除了为京东应用提供深色模式适配支持外,我们也期望基础组件的定位能够被更多应用所使用。除了支持现有功能外,Diablo的基础组件还支持个性化定制功能和API,让接入方可以根据自己的独特需求灵活选择。
应用内切换多任务快照定制更新程序自定义颜色模式
本文和图片来自网络,不代表火豚游戏立场,如若侵权请联系我们删除:https://www.huotun.com/game/626812.html