专栏首页macOS 开发学习Mac开发跬步积累(五): Dark Mode下适配你的UI界面

Mac开发跬步积累(五): Dark Mode下适配你的UI界面

图片来自Apple官方

macOS 10.14中,苹果在系统本身样式(Light (aqua) appearance )基础上推出了暗黑模式(dark appearance),这种模式下可以更突出显示应用窗口中的内容,让用户的关注焦点聚集在App本身的视图中以便获取更佳的视觉体验.关于AppKit中的系统视图,苹果默认已经进行了暗黑模式适配升级,但对于许多自定义的View,还是需要我们花一点点时间处理的.

0x00: 关于 NSAppearance

macOS 10.9+ 的时候,苹果就提供了NSAppearance这个类来协助AppKit管理App的UI控件. NSAppearance决定着AppKit如何渲染每个UI控件的效果,尤其是与颜色或者图片相关的部分.

  • App启动后会获取系统当前样式(Light Appearance 或者Dark Appearance).
  • NSWindow会继承App的appearance效果;
  • NSView会继承其父类或者NSWindowappearance效果;
  • 开发者可以设置App的整体或者部分appearance效果;
  • Appkit绘制UI控件时,会自动将当前的appearance赋值给控件的appearance(在当前线程中进行);
  • NSAppearance会影响 系统字体(font),颜色(color),文本(text),图片(image)的相关绘制路径(draw path)进而影响显示效果.

0x01: 颜色适配(NSColor)

当用户切换Light / Dark Appearance时,UI控件的颜色有着明显不同的效果.在macOS 10.14之前我们对于一个控件的颜色值经常使用硬编码方式,因此当appearance变化时,这些硬编码的色值就难以适应了.

Appearance变化时,关于NSColor的适配苹果官方给出两种简单并且易于实现的方案:

  1. 使用带有语义的Color: 那么问题来了,到底什么是带有语义的Color呢? 看一下苹果官方的原文: Semantic colors let you specify colors based on their intended usage, rather than on the actual color. 简单的说就是根据使用场景来描述颜色,而不使用确切的值来描述颜色. 我们以一个Label 的例子来看一下代码与效果:

设置labelColor

运行效果:

LabelColor 在Dark 和Light 模式下的效果

系统提供的语义Color可以参考苹果开发者文档中的:UI Element Colors 例如Label相关的有:labelColor , secondaryLabelColor,tertiaryLabelColor,quaternaryLabelColor等.

除了这些语义Color之外,系统还提供了一下可适配的Color,通常都以system+颜色方式命名.例子如下:

         NSColor.systemRed
         NSColor.systemBlue
         NSColor.systemGray
         NSColor.systemPink
  1. 使用Assets Color:(推荐) 更多时候我们希望能够有更多自己可以定义的颜色,这时系统提供的语义Color就会显得不够用,这时我们可以使用Assets Color,具体操作请参考下图示例:

Assets Color 设置

Appearance 说明 代码调用Assets Color: "Color"是在Assets 中创建的颜色名称

调用Assets Color 运行效果:

Assets Color 运行效果

0x02: 图片适配(NSImage)

App图片是非常重要的UI资源,为了在合适的Appearance下显示正确的图片,主要有下面的三种方式.

  1. Image Assets 使用Assets ImageAssets Color非常相似,具体请参考操作图例:

Assets Image Set

Assets Image 的适配场景(即当下面场景变化时,会Appkit会自动调整Image进行适配):

  • Screen resolution(屏幕分辨率): Appkit会自动根据当前屏幕的解析度选取最佳的image进行显示
  • Light and dark appearances (Appearance切换): Any Appearance中的图片会适配macOS全版本,Light和Dark 仅适用macOS10.14之后的版本
  • High contrast (高对比度): 使图片与周边的内容对比根据突出,仅能用于macOS10.14+之后的版本
  1. Template Images 使用模版图片也是一种常用的适配解决方案,典型的案例就是设置控件的icon(比如一个播放或者暂停的按钮).这种方法需要配合使用图片编辑软件(项目中的话通常就是UI设计师来处理)制作图片模版,具体使用仅需两个步骤即可:
  • UI设计师需要根据场景设计图片,但需要遵守如下规则:

template 设置规则 需要忽略的部分使用透明背景 需要显示的部分使用黑色或者部分透明的黑色

  • 设置图片的渲染模式为Template:

设置图片渲染模式

  1. Drawing Handler 使用NSImageinit(size:flipped:drawingHandler:)方法可以让Appkit根据appearance变化时自动调用drawingHandler中的代码进行图片创建,从而实现适配效果;

0x03: 自定义View 适配(NSView)

当改变当前的appearance时,AppKit会自动调用NSView的下面几个方法(根据情况调用)

  • updateLayer()
  • draw(_:)
  • layout()
  • updateConstraints() 这样我们就有机会在变更appearance时,通过重载上面的方法来实现自定义view的UI适配工作,示例代码如下:
override func updateLayer() {
   self.layer?.backgroundColor = NSColor.textBackgroundColor.cgColor

   // Other updates.
}

注意点!!! NSColor会立刻生效,但CGColor需要App再次启动才会生效!

0x04: 定制App的appearance(NSApp)

  • 设置NSView或者NSWindowappearance:

NSView Appearance 注意点!!! Appearance是存在继承关系的:NSApp->NSWindow->NSView

  • 通过代码方式设置NSViewappearance:
class MyContentView : NSView {
  func adoptAquaAppearance()
      self.appearance = NSAppearance(named: .aqua)
   }
}
  • 设置NSAppAppearance:
  NSApp.appearance = NSAppearance(named: .darkAqua)

0x05: Visual Effect View

关于NSVisualEffectViewAppearance适配,苹果官方建议采用根据使用明确场景语义枚举.例如在一个popOver的窗口中,推荐使用NSVisualEffectView.Material.popover,这样系统就根据appearance变化自动选择合适的效果了.同时系统也废弃了如下的枚举:

  • NSVisualEffectView.Material.light Deprecated
  • NSVisualEffectView.Material.dark Deprecated
  • NSVisualEffectView.Material.mediumLight Deprecated
  • NSVisualEffectView.Material.ultraDark Deprecated

0x06: 当appearance 切换时,应避免耗时操作

当切换系统的Appearance时,AppKit会同时更新UI控件,这部分工作通常都是自动完成的.但有时也会调用开发者编写的代码,例如你使用了NSImagedraw handler 方式创建图片对象,又或者使用了KVO监听一个视图或者窗口的effectiveAppearance属性,因此请需要注意下面几点:

  • 尽可能快的更新UI;
  • 不要执行与appearance变更无关的任务;
  • appearance变化时AppKit会自动添加过渡效果动画,但如果你的更新UI代码任务过重,AppKit将会丢弃过渡效果动画!

0x07: one more thing

为了考虑兼容macOS10.14之前的App版本,但又想支持Dark Appearance的效果,那么可以在Info.plist中添加 NSRequiresAquaSystemAppearancekey,并设置值为true即可. 这样做的前提是要保证App在macOS10.14的Dark Mode下可以正常适配UI效果~.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 五彩斑斓的黑

    Apple的黑暗模式已经横空出世很久国内蓝绿大厂也已经迎头赶上,大家已经吃到macOS下五彩斑斓的黑然鹅很多App依然没有适配出五彩斑斓的黑。比如楼主自己的Ma...

    大话swift
  • 苹果WWDC:让人想花钱!Mac Pro“硬”到尖叫,iPadOs系统独立,四大升级系统“软”出惊喜

    北京时间今日凌晨2点,一年一度的WWDC正式召开,再次一次性发布iOS 13、WatchOS 6、tvOS、macOS Mojave四大升级系统,还重磅发布iP...

    镁客网
  • 全网最详!暗黑模式在 Trip.com App 的实践

    本文为联合撰稿,作者为携程国际业务研发部UED团队静静,公共研发团队祥星、旭仔、俊仔、增翼。

    携程技术
  • DarkMode(1):产品应用深色模式分析

    2018 年的 macOS Mojave 率先支持了深色外观,紧接着 Windows 10 在 2018 年的 10 月份大版本更新中,也引入了 Dark Mo...

    周陆军
  • 苹果WWDC2018:暗黑界面的新版macOS发布,唯一“新产品”是只表带

    大数据文摘
  • 聊聊 React 组件库的技术选型与设计

    最近在业务中开发了一套定制化的 C 端组件库,在这个过程中遇到了一些组件库技术选型和设计的问题,在参考公司内外的多个组件库后确定了最终的方案。本文希望通过向读者...

    前端迷
  • 3.Pycharm设置开发模板/字体大小/背景颜色

    选择 File –> setting –> Editor –> Font ,可以看到如上界面,可以根据自己的喜好随意调整字体大小,字体风格,文字行间距,设置之后...

    猿说编程[Python和C]
  • 网站如何适配暗色模式并实现手动、自动切换

    那么,我们自己的网站如何适配暗色/亮色模式呢?首先说一下最基础的媒体查询,然后带大家了解一下我的适配方案(纯JS、CSS和HTML的前端操作)。

    Mintimate
  • 用tailwindcss适配暗黑模式竟如此简单

    接着上面一篇《从Nuxt文档里发现色彩的配搭诀窍》的内容,本文是我继续对 Nuxt/Content 的探索。当我研究它的色彩系统的时候,我还发现它还提供了暗黑模...

    秋风的笔记
  • 一篇文带你了解黑暗UI模式的过去,现在和未来

    在19年的WWDC期间,苹果在iOS上引入了 Dark Mode。关于这种模式,苹果公司的原话是,它是一种“戏剧性的新外观,可帮助您专注于工作”,以及“创建一种...

    用户5009027
  • 【CSS】515- 如何通过CSS向JS传参的

    CSS中有很多媒体查询的用法,例如设备尺寸判别,是否支持鼠标行为,是否是黑暗模式,是否是省电模式等。

    pingan8787
  • 3.python从hello world开始

    万丈高楼平地起,编程亦如此。改变世界是结果,坚持努力学习改bug是过程,hello world是开始,所有语言均是如此。

    猿说编程[Python和C]
  • 3.python从hello world开始

    万丈高楼平地起,编程亦如此。改变世界是结果,坚持努力学习改bug是过程,hello world是开始,所有语言均是如此。

    猿说编程[Python和C]
  • Mac Terminal emulator 终端神器

    Mac系统默认是自带终端的, 但用过之后你会发现功能仅能满足一些常规任务, 稍微有点要求的我们依然无法满足,于是我们就在寻找其他合适的利器。

    louiezhou001
  • Android App Dark Theme(暗黑模式)适配指南

    在 2019 年的 Google I/O 和 Apple WWDC 上,新露面的 Android 10 和 iOS 13 都宣布将支持 Dark Theme 也...

    Android丨Kotlin
  • Flutter之初体验

    Flutter是什么?他是谷歌根据Dark语言开源的跨平台开发依赖。和目前比较火的Reactive Native一样,一套代码能够实现两个不同平台的App。那么...

    蜻蜓队长
  • 24.python 文件读写操作

    以前的代码都是直接将数据输出到控制台,实际上我们也可以通过读/写文件的方式读取/输出到磁盘文件中,文件读写简称I/O操作。文件I/O操作一共分为四部分:打开(o...

    猿说编程[Python和C]
  • 20项调整升级!iOS 13新功能大剧透:家长可让孩子晚上只能和他们联系

    根据彭博社报道,苹果下一代移动操作系统iOS 13将会多达20项的调整升级,主要有:

    量子位
  • Flutter the Future

    Flutter Interact 2019在双12的凌晨结束了,6个多小时的大会,每一分钟都让一个开发者感到惊艳。

    用户1907613

扫码关注云+社区

领取腾讯云代金券