IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> iOS13.0上适配深色模式 -> 正文阅读

[移动开发]iOS13.0上适配深色模式

问题剖析

  • 首先,所有 UIKit 本身所提供的 UI 控件(例如 Tabbar) ,只要没有针对颜色等内容特殊设置过,都会自动适配深色模式,这部分是开发者无需关心的
  • 开发者可以通过 UIKit 在 UI 控件的颜色模糊效果图片这三个方面新提供的 API,来让自己的 App 适配深色模式,具体方式为:
    • 给 UI 控件设置颜色的时候,不要设置类似 UIColor.black 这样的绝对值颜色,而是设置 UIKit 中新提供的动态颜色(Dynamic Colors),比如 UIColor.systemBackground
    • 利用 UIVisualEffectView 来创建一些类似模糊的效果时,不要设置类似 UIBlurEffect.UIBlurEffectStyleExtraLight 这样带有明确颜色的效果,而是设置 UIKit 中新提供的动态样式的效果,比如 UIBlurEffect.systemThinMaterial
    • 利用 xcassets 管理图片和颜色的时候,如果有必要,开发者可以使用 xcassets 在 Xcode 11 中新增的功能,为深色模式额外指定一个图片或者颜色
  • 针对那些自定义的 UI 控件,开发者可以通过使用 UITraitCollection ,通过判断当前系统的颜色模式来完成对深色模式的适配

如何在 iOS 上适配深色模式?

解决两个问题:

  • 在 iOS 13 中,我们如何判断当前系统的颜色模式?
  • 在 iOS 13 中,我们应该对哪些 UI 上的内容适配深色模式?

如何判断当前系统的颜色模式?

在 iOS 13 中,我们可以通过 UITraitCollection 来判断当前系统的颜色模式。在 iOS 系统中,UITraitCollection 已经成为一个管理所有用户界面相关信息的大管家了:而颜色模式的相关信息,被存放在它的 userInterfaceStyle 属性中。

在 iOS 中,我们所熟悉的 UIView 和 UIViewController 、UIScreen、UIWindow 都已经遵从了 UITraitEnvironment 这个协议,因此这些类都拥有一个叫做 traitCollection 的属性,在这些类的方法中,我们可以这样去判断当前 App 的颜色模式:

let isDark = traitCollection.userInterfaceStyle == .dark

除此之外,我们还可以使用 UITraitCollection.current 这个类属性来获取当前 App 的颜色模式。不过需要注意的是在,并不是在所有的地方使用这个 API 都是正确的,只有在下面这些方法中,才可以放心的使用这个 API:

UIViewUIViewControllerUIPresentationController
draw()
layoutSubview()viewWillLayoutSubviews() viewDidLayoutSubviews()containerViewWillLayoutSubviews() containerViewDidLayoutSubviews()
traitCollectionDidChange() tintColorDidChange()traitCollectionDidChange()traitCollectionDidChange()

哪些内容是我们应该适配的?

  1. 颜色
  2. 模糊效果
  3. 图片

颜色

Apple Design Resources.

override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .white
        self.textView.textColor = .init(red: 0, green: 1, blue: 2, alpha: 1)
        self.textView.backgroundColor = UIColor(named: "textViewBackgroundColor")!
}

改:

extension UIColor {
    class var myDynamicColor: UIColor {
        get {
            .init(dynamicProvider: { (traitCollection) -> UIColor in
                if traitCollection.userInterfaceStyle == .dark {
                    return UIColor(displayP3Red: 0, green: 0, blue: 0, alpha: 1)
                } else {
                    return UIColor(displayP3Red: 255, green: 255, blue: 255, alpha: 1)
                }
            })
        }
    }
}
self.view.backgroundColor = .systemBackground
self.textView.textColor = .myDynamicColor
//而针对从 xcassets 中读取的颜色,在 iOS 13 中,我们可以通过 Xcode 11 的新功能,为 xcassets 中的颜色额外设置深色模式时的实际颜色

模糊效果

Apple Design Resources.

let blurView = UIVisualEffectView(effect: UIBlurEffect.init(style: .light))

//改:
let blurView = UIVisualEffectView(effect: UIBlurEffect.init(style: .systemMaterial))

图片

可以继续利用 xcassets 中图片新增的 Apperance 属性,分别设置两种模式下所使用到的图片。

另外需要注意的是,App 的启动屏也应该适配深色模式。如果我们的 App 使用了 LaunchImage 作为启动屏,那么就应该考虑把 LaunchImage 换成 LaunchScreen.storyboard 了,因为在 Xcode 11 中 LaunchImage 并不能像普通的图片那样针对深色模式设置另外的一张图片。

适配过程中需要关注的一些细节

Activity Indicator 和 Status Bar 的 API 变更

在 iOS 13 之前,Indicator系统提供给我们的属性有:

  • .gray
  • .white
  • .whitelarge

而在 iOS 13 中:

  • large
  • medium

如果对 ActivityIndicatorView 的颜色有配置的需求,苹果的工程师建议我们使用 color 属性

类似的,在 iOS 13 之前,状态栏的样式的枚举值也带有着明显的颜色倾向:

  • .default
  • .lightcontent

在 iOS 13 中,状态栏的 default 样式会根据当前的模式展示不同的颜色,而原有的 lightContent 样式则新增一个 darkContent 的样式与之对应:

  • .default
  • .lightcontent
  • .darkcontent

特殊的 Attributed String

在 UILabel、UITextField 以及 UITextView 中,如果我们没有设置任何 Attributed String,那么系统会帮我们做好控件中文字的深色模式适配;

请添加图片描述

但是要注意的是,如果这些控件中的文字包含 AttributedString,那么系统是不会默认为我们做任何适配工作的;
这种情况下我们需要给我们的 Attributed String 显示的加上一个动态颜色的前景色属性:
请添加图片描述

怎样快速切换深色模式?

在系统默认的操作流程中,如果我们想修改当前应用的预览模式,我们需要跳转到系统的设置中进行切换。在实际开发中这个过程是相当繁琐的。因此 Xcode 为我们提供了比较方便的调试功能。

如果我们使用的是 StoryBoard 开发的 UI 界面,我们可以在 StoryBoard 的底部的 Interface Style 中切换深色模式;

在应用启动后,我们还可以通过 Debug 面板中新增的 Environment Overrides 来快速切换到深色模式;

深色模式的背后:详细了解 UITraitCollection

为什么要有 UITraitCollection.current

关于 UITraitCollection,在前面我们并没有说到这样一个点:在 IOS 13 中,很多系统的 UI 组件都提供了利用 UITraitCollection 获取对应当前模式下实际表现对象的 API,比如:

let realColor = UIColor.systemBackground.resolvedColor(with: traitCollection)
let realImage = UIImage(named: "Header")?.imageAsset?.image(with: traitCollection)

似乎在任何地方,当我们想知道一个动态颜色的真实颜色时,传入一个 traitCollection 就可以了。但是我们可以注意到,当我们获取动态颜色的 GCColor 的时候,我们并不需要传入一个 traitCollection:

let someColor = UIColor.systemBackground
// ...
let realCGColor = someColor.cgColor

这是怎么做到的呢?在iOS 13 中,UIKIt 特地为 UITraitCollection 增加了 UITraitCollection.current 属性,让 UIColor 内部可以直接通过 UITraitCollection.current 获取到当前的 traitCollection,这样一来,当我们获取动态颜色的 CGColor 的时候,就不需要传入一个 traitCollection,这也保持了动态颜色和原有的绝对值颜色在接口设计上的统一性。

不过就像我们前面所说的,并不是任何时候我们都可以安全的使用 UITraitCollection.current 这个属性。只有我们在前面也提到的这些方法中(见上表),我们可以安心使用 UITraitCollection.current 这个属性(在这些方法执行前,UIKit 会自动帮我们将 UITraitCollection.current 设置为 self.traitCollection)

在这些方法之外,如果我们想要利用 UITraitCollection.current 这个属性,我们就需要自己来保证它和 self.traitCollection 保持一致 :

// 第一种做法
traitCollection.performAsCurrent {
        let colorOne = UIColor.label.cgColor
      // ...
}
// 第二种做法
let savedTraitCollection = UITraitCollection.current
UITraitCollection.current = traitCollection
let colorOne = UIColor.label.cgColor
// ...
UITraitCollection.current = savedTraitCollection

我们还能用 UITraitCollection 做什么?

在 iOS 中,App 中的 UITraitCollection 是和视图绑定的,每个视图自己都拥有一个自己的 UITraitCollection,如下图所示,视图中的每一个对象都拥有着自己的 UITraintCollection;
当我们在创建新的视图时,新创建的视图中的 UITraintCollection 会依据当前视图中的 UITraintCollection 来创建,其中的值相同,只是这是两个不同的 UITraitCollection 对象。我们可以通过 setOverrideTraitCollection(_:forChild:) 这个 API,来改变这个默认行为。这样一来,我们就可以单独的为某个子视图修改 traitCollection,从而让某个子视图拥有和其他视图不同的外观。

如果当前 App 的 UITraitCollection 产生了变化,我们也可以在 traitCollectionDidChange(_😃 这个回调方法中得到通知,然后根据 UITraitCollection 的变化来做出相应的应对:

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
      if previousTraitCollection?.hasDifferentColorAppearance(comparedTo: traitCollection) {
        // do something
  }
}

如果我们希望某一个视图层级中的所有某个下游的视图都切换到某个模式,类似这样:
请添加图片描述
只需要在 UIView 或者 UIViewContrller 中,修改 overrideUserInterfaceStyle 这个属性即可:

class UIViewContrller {
	var overrideUserInterfaceStyle:UIUserInterfaceStyle
}
class UIView {
	var overrideUserInterfaceStyle:UIUserInterfaceStyle
}

如果想让 App 全局都是用一种特定的模式,只要在 Info.plist 文件中将 UIUserInterfaceStyle 设置为 Light 或 Dark 即可。

如果我们想知道 UITraitCollection 在 App 中在什么地方变化了,我们也可以通过设置-UITraitCollectionChangeLoggingEnabled YES 这个启动选项来让 Xcode 在 UITraitCollection 变化的时候输出日志:
请添加图片描述

适配深色模式的一些建议

深色模式的适配,本质上其实是一个设计驱动的工作,iOS 13 中很多新的 API,例如 systemBackground、systemBlue、systemGray 等,都是针对最新的 HIG 中深色模式设计规范的一个具体的体现。

在实际的工作场景中,我们在适配深色模式的过程中一定会和我们的设计同学们有很多的沟通。在开始具体的沟通之前,我们可以建议他们首先去看看新的 HIG中深色模式的部分以及 What’s New in iOS Design 这个 Session。这样可以有更高的一个沟通效率。如果觉得这些内容太多,写给设计师的指南:iOS 13 Dark Mode 深度解析 这篇文章也是一个不错的选择。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-30 12:51:16  更:2021-07-30 12:52:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 7:10:59-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码