如何在iOS13.0上适配深色模式

  • 问题剖析
  • 如何在 iOS 上适配深色模式?
  • 如何判断当前系统的颜色模式?
  • 哪些内容是我们应该适配的?
  • 颜色
  • 模糊效果
  • 图片
  • 适配过程中需要关注的一些细节
    • Activity Indicator 和 Status Bar 的 API 变更
    • 特殊的 Attributed String
    • 怎样快速切换深色模式?
  • 深色模式的背后:详细了解 UITraitCollection
    • 为什么要有 UITraitCollection.current
    • 我们还能用 UITraitCollection 做什么?
    • 适配深色模式的一些建议

问题剖析

  • 首先,所有 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:

UIView UIViewController UIPresentationController
draw()
layoutSubview() viewWillLayoutSubviews() viewDidLayoutSubviews() containerViewWillLayoutSubviews() containerViewDidLayoutSubviews()
traitCollectionDidChange() tintColorDidChange() traitCollectionDidChange() traitCollectionDidChange()

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

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

颜色

Apple Design Resources.

override func viewDidLoad() {super.viewDidLoad()self.view.backgroundColor = .whiteself.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 inif 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(_

iOS13.0上适配深色模式相关推荐

  1. iOS13适配深色模式(Dark Mode)

    原文博客地址: iOS13适配深色模式(Dark Mode) 好像大概也许是一年前, Mac OS系统发布了深色模式外观, 看着挺刺激, 时至今日用着也还挺爽的 终于, 随着iPhone11等新手机的 ...

  2. iOS13适配深色模式(Dark Mode)总结

    iOS13适配深色模式(Dark Mode)总结 好像大概也许是一年前, Mac OS系统发布了深色模式外观, 看着挺刺激, 时至今日用着也还挺爽的 终于,随着iPhone11等新手机的发售, iOS ...

  3. Flutter适配深色模式(DarkMode)

    1.瞎叨叨 也不知道写点什么,本来想写写Flutter的集成测试.因为前一阵子给flutter_deer写了一套,不过感觉也没啥内容,写不了几句话就放弃了.(其实本篇内容也不多...) 那就写写最近在 ...

  4. 实现页面适配_微信公众号文章页面适配深色模式

    最近安卓微信7.0.10正式版发布,更新过后,很多用户发现,之前在测试版中对系统深色模式的适配功能被取消了,小伙伴们对此很是不满,好在Android 10系统手机用户占比很少,影响范围还不是很大,并且 ...

  5. android开发适配深色模式,手机不支持深色模式,如何用软件解决深色模式的问题?(附有系统全局深色模式实现方法...

    本帖最后由 巷子口的你 于 2020-8-8 07:57 编辑 1.92允许通过设置为助手应用来饮捷切频深色模式(设置入口一般为系统默认应用-助手和语音输人, MIU需要设置为语音助手)提醒:稳定模式 ...

  6. android自动切换暗色,Android 适配深色模式的总结

    Android Q 推出了深色模式,其实 Android 9 就有了,部分厂商小米,三星就在系统 Android 9 加入了深色模式的开关. Android 提供了一套夜间模式主题,继承 Theme. ...

  7. mac os上onenote深色模式dark mode如何开启 2019

    首先确保你的系统是深色模式 然后在onenote的偏好设置中"启用试验性功能" 最后,重启onenote,然后你就会看见深色了

  8. SVG公众号排版『适配深色模式图片二维码可识别可点击』模板代码

    二维码可识别图片不弹出 <!DOCTYPE html> <html> <head><meta charset="UTF-8" />& ...

  9. 微信7.0.10内测更新!除了适配暗黑模式,还有这些实用功能!

    微信7.0.10内测更新,其实本没打算写这篇文章~ 因为,我自己在使用魅族手机,早都可以愉快地使用暗黑模式了~对于,这次更新适配暗黑模式,并没有那么兴奋! 不过,我还是第一时间下载更新体验一下,说说有 ...

  10. iOS微信7.0.12发布!除了适配暗黑模式,还有这些新功能!

    前几天,"微信不适配深色模式就会被下架"的消息闹得沸沸扬扬~最后,腾讯微信团队官宣"与苹果达成合作,共同探索微信在iOS系统的暗黑模式体验,有望在下一个版本中上线!&qu ...

最新文章

  1. 【Python】学习笔记总结(第二阶段(7-9)——汇总篇)
  2. android launcher主要功能_[Android] 自动收取蚂蚁森林能量
  3. Android Studio-AndroidStudio目录结构
  4. 8个使用JavaScript展示图片解决方案
  5. ecshop使用php代码,ecshop 修改模板可输出php代码
  6. android:contentDescription 的用途
  7. for循环 与 while循环
  8. python黑客帝国代码雨源代码_黑客帝国数字雨 源代码分享
  9. 微信小程序中英文切换
  10. php array_change_key_case()
  11. JavaAwtSwing笔记之 Frame和JFrame的区别
  12. 图扑数字孪生智慧加油站,构建安全防护网
  13. 怎么给word文档注音_Word文档中,怎样全篇加注拼音?
  14. 戴尔笔记本无线网络无法连接
  15. 将文件从VMWare虚拟机的Linux系统传到U盘
  16. MySQL 百万级/千万级表 全量更新
  17. GRAIL Efficient Time Series Representation Learning论文阅读笔记(三)
  18. 深度学习入门笔记(七):深层神经网络
  19. 元境技术助力元宇宙营销 联合发起商广协元宇宙营销研究院
  20. Numpy库 numpy.corrcoef()函数

热门文章

  1. python 频谱图_SciPy spectrogram:计算频谱图
  2. MB/s MiB/s之间换算
  3. 金融专业英语词汇大全
  4. Unity 风吹草的实现
  5. 《即兴演讲》学习总结
  6. 资产类别某一类折旧查询
  7. 第三次打卡 特征工程
  8. 【原创】软件测试(原书第二版)
  9. 如何注册 MSDN AA
  10. 这份免税的农产品销售发票,可以抵扣9%的增值税吗?