iOS13 暗黑模式(Dark Mode)适配之OC版

2024-06-01 22:38

本文主要是介绍iOS13 暗黑模式(Dark Mode)适配之OC版,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 一、适配Dark Mode
    • 颜色适配
    • 图片适配
  • 二、获取当前模式(Light or Dark)
  • 三、其他内容
  • 四、总结

首先看看我们的效果图:

适配效果图

一、适配Dark Mode

开发者主要从颜色和图片两个方面进行适配,我们不需要关心切换模式时该如何操作,这些都由系统帮我们实现

1 颜色适配

  • iOS13 之前 UIColor只能表示一种颜色,而从 iOS13 开始UIColor是一个动态的颜色,在Light ModeDark Mode可以分别设置不同的颜色。
  • iOS13系统提供了一些动态颜色
  •  @property (class, nonatomic, readonly) UIColor *systemBrownColor        API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);

    @property (class, nonatomic, readonly) UIColor *systemIndigoColor       API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);

    @property (class, nonatomic, readonly) UIColor *systemGray2Color        API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);

    @property (class, nonatomic, readonly) UIColor *systemGray3Color        API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);

    @property (class, nonatomic, readonly) UIColor *systemGray4Color        API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);

    @property (class, nonatomic, readonly) UIColor *systemGray5Color        API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);

    @property (class, nonatomic, readonly) UIColor *systemGray6Color        API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);

    @property (class, nonatomic, readonly) UIColor *labelColor              API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);

    @property (class, nonatomic, readonly) UIColor *secondaryLabelColor     API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);

    @property (class, nonatomic, readonly) UIColor *tertiaryLabelColor      API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);

    @property (class, nonatomic, readonly) UIColor *quaternaryLabelColor    API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);

    @property (class, nonatomic, readonly) UIColor *linkColor               API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);

    @property (class, nonatomic, readonly) UIColor *placeholderTextColor    API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);

    @property (class, nonatomic, readonly) UIColor *separatorColor          API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);

    @property (class, nonatomic, readonly) UIColor *opaqueSeparatorColor    API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);

    @property (class, nonatomic, readonly) UIColor *systemBackgroundColor                   API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);

    @property (class, nonatomic, readonly) UIColor *secondarySystemBackgroundColor          API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);

    @property (class, nonatomic, readonly) UIColor *tertiarySystemBackgroundColor           API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);

    @property (class, nonatomic, readonly) UIColor *systemGroupedBackgroundColor            API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);

    @property (class, nonatomic, readonly) UIColor *secondarySystemGroupedBackgroundColor   API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);

    @property (class, nonatomic, readonly) UIColor *tertiarySystemGroupedBackgroundColor    API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);

    @property (class, nonatomic, readonly) UIColor *systemFillColor                         API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);

    @property (class, nonatomic, readonly) UIColor *secondarySystemFillColor                API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);

    @property (class, nonatomic, readonly) UIColor *tertiarySystemFillColor                 API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);

    @property (class, nonatomic, readonly) UIColor *quaternarySystemFillColor               API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);

  • ① 实例
  • [self.view setBackgroundColor:[UIColor systemBackgroundColor]];

    [self.titleLabel setTextColor:[UIColor labelColor]];

    [self.detailLabel setTextColor:[UIColor placeholderTextColor]];

  • ② 效果展示

系统UIColor样式

用法和iOS13之前的一样,使用系统提供的这些动态颜色,不需要其他的适配操作

③ 自定义动态UIColor

在实际开发过程,系统提供的这些颜色还远远不够,因此我们需要创建更多的动态颜色

初始化动态UIColor方法

iOS13 UIColor增加了两个初始化方法,使用以下方法可以创建动态UIColor
注:一个是类方法,一个是实例方法

+ (UIColor *)colorWithDynamicProvider:(UIColor * (^)(UITraitCollection *))dynamicProvider API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);

- (UIColor *)initWithDynamicProvider:(UIColor * (^)(UITraitCollection *))dynamicProvider API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos); 

  • 这两个方法要求传一个block进去
  • 当系统在LightModeDarkMode之间相互切换时就会触发此回调
  • 这个block会返回一个UITraitCollection
  • 我们需要使用其属性userInterfaceStyle,它是一个枚举类型,会告诉我们当前是LightMode还是DarkMode
  • typedef NS_ENUM(NSInteger, UIUserInterfaceStyle) {

        UIUserInterfaceStyleUnspecified,

        UIUserInterfaceStyleLight,

        UIUserInterfaceStyleDark,

    } API_AVAILABLE(tvos(10.0

  • 实例

  •  

    UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) {

            if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {

                return [UIColor redColor];

            }

            else {

                return [UIColor greenColor];

            }

        }];

        

    [self.bgView setBackgroundColor:dyColor]

效果展示

自定义UIColor效果

接下来我们看看如何适配图片

2.图片适配

  • 打开Assets.xcassets
  • 新建一个Image set

默认显示效果

  • 打开右侧工具栏,点击最后一栏,找到Appearances,选择Any,Dark

    侧边栏

  • 将两种模式下不同的图片资源都拖进去

两种不同模式

  • 使用该图片
  • [_logoImage setImage:[UIImage imageNamed:@"icon_logo"]];

最终效果图

大功告成,完成颜色和图片的Dark Mode适配,是不是很easy呢

① 获取当前模式(Light or Dark)

有时候我们需要知道当前处于什么模式,并根据不同的模式执行不同的操作
iOS13中CGColor依然只能表示单一的颜色
通过调用UITraitCollection.currentTraitCollection.userInterfaceStyle
获取当前模式

实例

if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {

        [self.titleLabel setText:@"DarkMode"];

    }

    else {

        [self.titleLabel setText:@"LightMode"];

    }

 

三、其他

1.监听模式切换

有时我们需要监听系统模式的变化,并作出响应
那么我们就需要在需要监听的viewController中,重写下列函数

 

// 注意:参数为变化前的traitCollection

- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection;

 

// 判断两个UITraitCollection对象是否不同

- (BOOL)hasDifferentColorAppearanceComparedToTraitCollection:(UITraitCollection *)traitCollection;

① 示例

- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {

    [super traitCollectionDidChange:previousTraitCollection];

    // trait发生了改变

    if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) {

    // 执行操作

    }

    } 

2.CGColor适配

我们知道iOS13后,UIColor能够表示动态颜色,但是CGColor依然只能表示一种颜色,那么对于CALayer等对象如何适配暗黑模式呢?当然是利用上一节提到的监听模式切换的方法啦。

① 方式一:resolvedColor

// 通过当前traitCollection得到对应UIColor

// 将UIColor转换为CGColor

- (UIColor *)resolvedColorWithTraitCollection:(UITraitCollection *)traitCollection;实例

- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {

    [super traitCollectionDidChange:previousTraitCollection];

    

    UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) {

        if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {

            return [UIColor redColor];

        }

        else {

            return [UIColor greenColor];

        }

    }];

    UIColor *resolvedColor = [dyColor resolvedColorWithTraitCollection:previousTraitCollection];

    layer.backgroundColor = resolvedColor.CGColor;

② 方式二:performAsCurrent

 

// 使用当前trainCollection调用此方法

- (void)performAsCurrentTraitCollection:(void (^)(void))actions;

示例

- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {

    [super traitCollectionDidChange:previousTraitCollection];

    

    UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) {

        if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {

            return [UIColor redColor];

        }

        else {

            return [UIColor greenColor];

        }

    }];

    [self.traitCollection performAsCurrentTraitCollection:^{

        layer.backgroundColor = dyColor.CGColor;

    }];

    

}

方式三:最简单的方法

直接设置为一个动态UIColor的CGColor即可

- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {

    [super traitCollectionDidChange:previousTraitCollection];

    UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) {

        if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {

            return [UIColor redColor];

        }

        else {

            return [UIColor greenColor];

        }

    }];

        layer.backgroundColor = dyColor.CGColor;

}

⚠️!!! 设置layer颜色都是在traitCollectionDidChange中,意味着如果没有发生模式切换,layer将会没有颜色,需要设置一个基本颜色

3.模式切换时打印log

模式切换时自动打印log,就不需要我们一次又一次的执行po命令了

  • 在Xcode菜单栏Product->Scheme->Edit Scheme
  • 选择Run->Arguments->Arguments Passed On Launch
  • 添加以下命令即可
    • -UITraitCollectionChangeLoggingEnabled YES

      模式切换打印log

4.强行设置App模式

当系统设置为Light Mode时,对某些App的个别页面希望一直显示Dark Mode下的样式,这个时候就需要强行设置当前ViewController的模式了

// 设置当前view或viewCongtroller的模式

@property(nonatomic) UIUserInterfaceStyle overrideUserInterfaceStyle;

示例

// 设置为Dark Mode即可

[self setOverrideUserInterfaceStyle:UIUserInterfaceStyleDark];

⚠️ 注意!!!

  • 当我们强行设置当前viewControllerDark Mode后,这个viewController下的view都是Dark Mode
  • 由这个ViewController present出的ViewController不会受到影响,依然跟随系统的模式
  • 要想一键设置App下所有的ViewController都是Dark Mode,请直接在Window上执行overrideUserInterfaceStyle
  • window.rootViewController强行设置Dark Mode也不会影响后续present出的ViewController的模式

5.NSAttributedString优化

对于UILabel、UITextField、UITextView,在设置NSAttributedString时也要考虑适配Dark Mode,否则在切换模式时会与背景色融合,造成不好的体验

不建议的做法

NSDictionary *dic = @{NSFontAttributeName:[UIFont systemFontOfSize:16]};

NSAttributedString *str = [[NSAttributedString alloc] initWithString:@"富文本文案" attributes:dic];推荐的做法

// 添加一个NSForegroundColorAttributeName属性

NSDictionary *dic = @{NSFontAttributeName:[UIFont systemFontOfSize:16],NSForegroundColorAttributeName:[UIColor labelColor]};

NSAttributedString *str = [[NSAttributedString alloc] initWithString:@"富文本文案" attributes:dic];

五、总结

总的来说,iOS13主要有以下变化:
1.支持 Dark Mode
2.UIColor变为动态颜色
3.更新StatusBar样式
4.更新UIActivityIndicatorView样式

完整iOS13新特性请参考以下文章:
iOS13 新特性简介
iOS13-适配夜间模式/深色外观(Dark Mode)
)

iOS13最主要的是推出了暗黑模式Dark Mode,目前App Store榜单上的App已经开始积极适配了
9月份会发布iOS13正式版本,2020年苹果可能要求开发者必须适配Dark Mode,否则不予上架
Status Bar 样式被修改
UIActivityIndicatorView 原有的三种样式全部被废弃,推出两种新的样式

这篇关于iOS13 暗黑模式(Dark Mode)适配之OC版的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1022295

相关文章

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

模版方法模式template method

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/template-method 超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。 上层接口有默认实现的方法和子类需要自己实现的方法

【iOS】MVC模式

MVC模式 MVC模式MVC模式demo MVC模式 MVC模式全称为model(模型)view(视图)controller(控制器),他分为三个不同的层分别负责不同的职责。 View:该层用于存放视图,该层中我们可以对页面及控件进行布局。Model:模型一般都拥有很好的可复用性,在该层中,我们可以统一管理一些数据。Controlller:该层充当一个CPU的功能,即该应用程序

迭代器模式iterator

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/iterator 不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素

《x86汇编语言:从实模式到保护模式》视频来了

《x86汇编语言:从实模式到保护模式》视频来了 很多朋友留言,说我的专栏《x86汇编语言:从实模式到保护模式》写得很详细,还有的朋友希望我能写得更细,最好是覆盖全书的所有章节。 毕竟我不是作者,只有作者的解读才是最权威的。 当初我学习这本书的时候,只能靠自己摸索,网上搜不到什么好资源。 如果你正在学这本书或者汇编语言,那你有福气了。 本书作者李忠老师,以此书为蓝本,录制了全套视频。 试

利用命令模式构建高效的手游后端架构

在现代手游开发中,后端架构的设计对于支持高并发、快速迭代和复杂游戏逻辑至关重要。命令模式作为一种行为设计模式,可以有效地解耦请求的发起者与接收者,提升系统的可维护性和扩展性。本文将深入探讨如何利用命令模式构建一个强大且灵活的手游后端架构。 1. 命令模式的概念与优势 命令模式通过将请求封装为对象,使得请求的发起者和接收者之间的耦合度降低。这种模式的主要优势包括: 解耦请求发起者与处理者

springboot实战学习(1)(开发模式与环境)

目录 一、实战学习的引言 (1)前后端的大致学习模块 (2)后端 (3)前端 二、开发模式 一、实战学习的引言 (1)前后端的大致学习模块 (2)后端 Validation:做参数校验Mybatis:做数据库的操作Redis:做缓存Junit:单元测试项目部署:springboot项目部署相关的知识 (3)前端 Vite:Vue项目的脚手架Router:路由Pina:状态管理Eleme

状态模式state

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/state 在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。 在状态模式中,player.getState()获取的是player的当前状态,通常是一个实现了状态接口的对象。 onPlay()是状态模式中定义的一个方法,不同状态下(例如“正在播放”、“暂停

软件架构模式:5 分钟阅读

原文: https://orkhanscience.medium.com/software-architecture-patterns-5-mins-read-e9e3c8eb47d2 软件架构模式:5 分钟阅读 当有人潜入软件工程世界时,有一天他需要学习软件架构模式的基础知识。当我刚接触编码时,我不知道从哪里获得简要介绍现有架构模式的资源,这样它就不会太详细和混乱,而是非常抽象和易

使用Spring Boot集成Spring Data JPA和单例模式构建库存管理系统

引言 在企业级应用开发中,数据库操作是非常重要的一环。Spring Data JPA提供了一种简化的方式来进行数据库交互,它使得开发者无需编写复杂的JPA代码就可以完成常见的CRUD操作。此外,设计模式如单例模式可以帮助我们更好地管理和控制对象的创建过程,从而提高系统的性能和可维护性。本文将展示如何结合Spring Boot、Spring Data JPA以及单例模式来构建一个基本的库存管理系统