记一次修复模式切换引起的页面显示异常问题

如今的OS X app 必须要支持模式的切换,否则 review 过程中会被拒。从技术的角度,要适配 dark mode,主要是注意颜色和图片:

  1. 颜色:使用系统提供的“语义”颜色API,或者添加支持各个模式下的颜色集
  2. 图片:添加各个模式下的图片集

这里不从头讨论如何去实现,而是记录一次和模式切换相关的页面组建显示异常问题。

问题描述

App 使用了Split布局,左边区域的 NSViewController 中包含的组件有 NSTextField, NSButtonNSTextView 。当从明亮或黑暗模式启动时,窗口内容显示正常。当从设置中,修改当前的外观时,发现 NSTextFieldNSButton 中的文字和背景无法分辨,而 NSTextView 却正常显示。

排查过程

第一反应是打开 storyboard , 选中 ViewController,然后点击下方的 View as。此时,发现单独 dark 或 light appearance,页面正常显示。同时,确认了每个控件使用的颜色都是 semantic color API。
接下来,重启了系统。因为刚好因为系统空间不够,大刀阔斧地删除了一些文件,包括 Developer 下的内容。当然,重启后,并没有解决。 下一步,决定在相关的 ViewController 中,重载 viewWillLayout,在其中显式地为控件颜色赋值,但仍然无果:窗口内的控件依然不能正确显示。
接着,打算使用 Xcode 的 “Debug View Hierarchy”,谁知一点击,Xcode 就 crash......
为了好好地静一静,又看了下文档 Supporting Dark Mode in Your Interface,看着看着意识到控件的appearance通常是一次继承的:

NSApp->NSWindow->NSViewController->NSView

那是否有可能是上级 ViewController 或者 Window 中进行了某些操作呢?一查看,如果发现在某个父级 ViewController 中,用如下方式修改了 view 背景色:

    self.view.wantsLayer = true
    self.view.layer?.backgroundColor = NSColor.lightGray.cgColor

注意这种通过修改 CALayer 的方式即使使用 semantic color,如 controlBackgroundColor 也不会起作用。最后,将上述部分删除。

最后,想强调的是, NSViewController 和 UIViewController 不同,修改对应的 view 的背景色在 Cocoa 上,建议新建 NSView,在 updateLayer 中进行修改, 如:

override func updateLayer() {
   self.layer?.backgroundColor = NSColor.textBackgroundColor.cgColor

   // Other updates.
}