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

作者: 代码行者 | 来源:发表于2018-11-19 15:41 被阅读19次
    图片来自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效果~.

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/umtqtqtx.html