MacOS开发 技术集锦

macOS 暗黑模式

2021-07-17  本文已影响0人  goyohol

之前写了iOS 13下暗黑(深色)模式的配置一篇关于‘iOS的暗黑模式适配’的文章~
这次讨论一下macOS环境下的暗黑模式~(macOS 10.14)

暗黑模式活跃状态时,代码上可更新颜色图像行为—以便让应用程序可以自动适应。

在macOS和iOS中,用户可以选择采用全系统的明亮暗黑界面外观。这种被称为“暗黑模式”(dark Mode)的黑色外观,实现了许多应用程序已经采用的界面风格。用户可以选择他们喜欢的明亮暗黑界面外观,也可以根据环境照明条件特定的时间表来选择切换他们的界面

图片来自Apple

选择 模式(浅色/深色/自动)

进入'系统偏好设置'的'通用'项—就可以选择相应的模式(浅色/深色/自动)了~

'系统偏好设置'的'通用'

各种模式(浅色/深色/自动)及选中效果,如下:

浅色 深色 自动—根据'根据环境照明条件'或'特定的时间表'

关于 系统颜色

如下,macOS、iOS、tvOS系统颜色

macOS system Colors iOS system Colors tvOS system Colors

讨论下macOS系统颜色,及其在相应模式下的展示效果
在Nib控件中,查看颜色:

查看颜色(Nib控件中查看)

在'ViewController.swift'文件中:(在viewDidAppear方法内书写如下逻辑代码)

override func viewDidAppear() {
   super.viewDidAppear()
   
   let title_color_Arr: [String] = ["NSColor.labelColor", "NSColor.secondaryLabelColor", "NSColor.tertiaryLabelColor", "NSColor.quaternaryLabelColor", "NSColor.systemRed", "NSColor.systemGreen", "NSColor.systemBlue", "NSColor.systemOrange", "NSColor.systemYellow", "NSColor.systemBrown", "NSColor.systemPink", "NSColor.systemPurple", "NSColor.systemTeal", "NSColor.systemIndigo", "NSColor.systemGray", "NSColor.linkColor", "NSColor.placeholderTextColor", "NSColor.windowFrameColor", "NSColor.selectedMenuItemTextColor", "NSColor.alternateSelectedControlTextColor", "NSColor.headerTextColor", "NSColor.separatorColor", "NSColor.gridColor", "NSColor.textColor", "NSColor.textBackgroundColor", "NSColor.selectedTextColor", "NSColor.selectedTextBackgroundColor", "NSColor.selectedTextColor", "NSColor.selectedTextBackgroundColor", "NSColor.unemphasizedSelectedTextBackgroundColor", "NSColor.unemphasizedSelectedTextBackgroundColor", "NSColor.windowBackgroundColor", "NSColor.underPageBackgroundColor", "NSColor.controlBackgroundColor", "NSColor.selectedContentBackgroundColor", "NSColor.unemphasizedSelectedContentBackgroundColor", "NSColor.findHighlightColor", "NSColor.controlColor", "NSColor.controlTextColor", "NSColor.selectedControlColor", "NSColor.selectedControlTextColor", "NSColor.disabledControlTextColor", "NSColor.keyboardFocusIndicatorColor", "NSColor.controlAccentColor"]
   let v_color_Arr: [NSColor] = [NSColor.labelColor, NSColor.secondaryLabelColor, NSColor.tertiaryLabelColor, NSColor.quaternaryLabelColor, NSColor.systemRed, NSColor.systemGreen, NSColor.systemBlue, NSColor.systemOrange, NSColor.systemYellow, NSColor.systemBrown, NSColor.systemPink, NSColor.systemPurple, NSColor.systemTeal, NSColor.systemIndigo, NSColor.systemGray, NSColor.linkColor, NSColor.placeholderTextColor, NSColor.windowFrameColor, NSColor.selectedMenuItemTextColor, NSColor.alternateSelectedControlTextColor, NSColor.headerTextColor, NSColor.separatorColor, NSColor.gridColor, NSColor.textColor, NSColor.textBackgroundColor, NSColor.selectedTextColor, NSColor.selectedTextBackgroundColor, NSColor.selectedTextColor, NSColor.selectedTextBackgroundColor, NSColor.unemphasizedSelectedTextBackgroundColor, NSColor.unemphasizedSelectedTextBackgroundColor, NSColor.windowBackgroundColor, NSColor.underPageBackgroundColor, NSColor.controlBackgroundColor, NSColor.selectedContentBackgroundColor, NSColor.unemphasizedSelectedContentBackgroundColor, NSColor.findHighlightColor, NSColor.controlColor, NSColor.controlTextColor, NSColor.selectedControlColor, NSColor.selectedControlTextColor, NSColor.disabledControlTextColor, NSColor.keyboardFocusIndicatorColor, NSColor.controlAccentColor]
   //NSColor.alternatingContentBackgroundColors    //[NSColor]数组
   
   //let total_W: CGFloat = self.view.window?.contentView?.frame.size.width ?? 0//获取到的窗口宽度——需要在`viewDidAppear`方法中
   let total_W: CGFloat = 1500.0//总宽度
   let margin: CGFloat = 25.0
   let countNum: CGFloat = 5//每一行里面的个数
   let item_W: CGFloat = (total_W - (countNum + 1)*margin)/countNum
   let item_H: CGFloat = 30
   for i in 0..<v_color_Arr.count {
       print(i)
       let x = CGFloat(i%Int(countNum)) * (item_W + margin) + margin
       let y = CGFloat(i/Int(countNum)) * (item_H + margin) + margin
       let showV = NSView(frame: NSMakeRect(x, y, item_W, item_H))
       self.view .addSubview(showV)
       showV.wantsLayer = true
      let showColor = v_color_Arr[i]
      showV.layer?.backgroundColor = showColor.cgColor
       showV.layer?.borderWidth = 2.0; showV.layer?.borderColor = NSColor.red.cgColor

       let lb_margin: CGFloat = 3.0
       let lb_W: CGFloat = item_W
       let lb_H: CGFloat = margin - 2*lb_margin
       let tf = NSTextField(frame: NSMakeRect(showV.frame.minX, showV.frame.minY - lb_margin - lb_H, lb_W, lb_H))
       self.view .addSubview(tf)
       tf.isEditable = false
       tf.stringValue = title_color_Arr[i]//标题——该颜色
       tf.backgroundColor = NSColor.red
   }
   
}


效果

  • 1.'浅色'模式时,运行工程——启动App
    1-1.'浅色'模式时启动App

    1-1.'浅色'模式时启动App

    1-2.'浅色'模式时启动App,再在'通用"中切为'深色'模式

    1-2.'浅色'模式时启动App,再切为'深色'模式

  • 2.'深色'模式时,运行工程——启动App
    2-1.'深色'模式时启动App

    2-1.'深色'模式时启动App

    2-2.'深色'模式时启动App,再在'通用"中切为'浅色'模式

    2-2.'深色'模式时启动App,再切为'浅色'模式


结论
a.部分颜色在'深色'模式和'浅色'模式展示不同!(labelColorunderPageBackgroundColorselectedControlTextColor等……)
b.还有少部分颜色在App运行后,再切换'深色'/'浅色'模式时会有变化!(separatorColorsecondaryLabelColortertiaryLabelColorquaternaryLabelColor等……)



[A].UI上设置颜色
浅色深色界面模式使用非常不同的调色板。在浅色下效果很好的颜色在深色下可能很难被看到,反之亦然。一个自适应颜色对象为不同的界面模式返回不同的颜色值。

如下,有两种方法可以创建自适应颜色对象:


Tips:使用特定方法更新 自定义视图颜色
当用户更改系统外观时,系统会自动要求每个窗口和视图重新绘制自己。在此过程中,系统会调用下表中列出的几个macOS和iOS常用方法更新内容
在这些方法之外进行了外观敏感更改,那么应用程序可能无法为当前环境正确绘制其内容。解决方案是:将外观敏感更改代码移入到这些方法中。

类及其方法
🌰例子

自定义一个视图类GYHDefineView:

GYHDefineView

在'GYHDefineView.swift'文件中,重写draw方法:(进行外观敏感更改代码放在里面)

import Cocoa

class GYHDefineView: NSView {

   override func draw(_ dirtyRect: NSRect) {
       super.draw(dirtyRect)

       // Drawing code here.
       self.wantsLayer = true
       //let useColor = NSColor(named: "GYHViewColor")//有效果
       let useColor = NSColor(named: NSColor.Name("GYHViewColor"))//官方推荐的写法
       self.layer?.backgroundColor = useColor?.cgColor
   }
   
}

在'ViewController.swift'文件中使用GYHDefineView实例~

override func viewDidLoad() {
 super.viewDidLoad()
 let defineColor_V = GYHDefineView(frame: NSMakeRect(20, 10, 150, 50))
 self.view .addSubview(defineColor_V)
 
}

效果:启动App后切换'浅色'模式和'深色'模式颜色 会发生变化



[B].图片配置相应'浅色'/'深色'模式下的展示图(启动App后切换'浅色'模式和'深色'模式展示图 会发生变化

图片配置相应'浅色'/'深色'模式下的展示图:(如下,配置好'Appearance')

未配置'Appearance'时 配置好了'Appearance'的图片

代码使用

override func viewDidLoad() {
  super.viewDidLoad()
  let defineColor_V = NSView(frame: NSMakeRect(20, 10, 150, 50))
  self.view .addSubview(defineColor_V)
  defineColor_V.wantsLayer = true
  //let useColor = NSColor(named: "GYHViewColor")//有效果
  let useColor = NSColor(named: NSColor.Name("GYHViewColor"))//官方推荐的写法
  defineColor_V.layer?.backgroundColor = useColor?.cgColor
  
  let imgV = NSImageView(frame: NSMakeRect(20, 10, 150, 50))
  self.view .addSubview(imgV)
  imgV.image = NSImage(named: "Picture")
}

效果:启动App后切换'浅色'模式和'深色'模式展示图 会发生变化


更多,参考Providing Images for Different Appearances



[C].代码判断当前是否为暗黑模式:

//判断——当前是否为暗黑模式
func checkIsDark() -> Bool {
    let apperance = NSApp.effectiveAppearance;
    if #available(macOS 10.14, *) {
        if apperance .bestMatch(from: [NSAppearance.Name.darkAqua, NSAppearance.Name.aqua]) == NSAppearance.Name.darkAqua {
            return true  //'深色'模式
        }
    }
    return false  //'浅色'模式
}

更多NSAppearance.Name枚举值

extension NSAppearance.Name {

    
    @available(macOS 10.9, *)
    public static let aqua: NSAppearance.Name

    @available(macOS 10.14, *)
    public static let darkAqua: NSAppearance.Name

    
    @available(macOS, introduced: 10.9, deprecated: 10.10, message: "Light content should use the default Aqua apppearance.")
    public static let lightContent: NSAppearance.Name

    
    /* The following two Vibrant appearances should only be set on an NSVisualEffectView, or one of its container subviews.
     */
    @available(macOS 10.10, *)
    public static let vibrantDark: NSAppearance.Name

    @available(macOS 10.10, *)
    public static let vibrantLight: NSAppearance.Name

    
    /* The following appearance names are for matching using bestMatchFromAppearancesWithNames:
       Passing any of them to appearanceNamed: will return NULL
     */
    @available(macOS 10.14, *)
    public static let accessibilityHighContrastAqua: NSAppearance.Name

    @available(macOS 10.14, *)
    public static let accessibilityHighContrastDarkAqua: NSAppearance.Name

    @available(macOS 10.14, *)
    public static let accessibilityHighContrastVibrantLight: NSAppearance.Name

    @available(macOS 10.14, *)
    public static let accessibilityHighContrastVibrantDark: NSAppearance.Name
}


[D].通过代码设置当前模式'深色'模式'浅色'模式
设置NSApplication .shared.appearanceNSApp.appearance属性

//设置——'深色'模式、'浅色'模式
func setNowToIsDark(isDark: Bool) {
    if isDark {
        NSApplication .shared.appearance = NSAppearance(named: NSAppearance.Name.darkAqua)
        //NSApp.appearance = NSAppearance(named: NSAppearance.Name.darkAqua)
    } else {
        NSApplication .shared.appearance = NSAppearance(named: NSAppearance.Name.aqua)
        //NSApp.appearance = NSAppearance(named: NSAppearance.Name.aqua)
    }
}



[E].选择暂时退出 暗黑模式Opt Out of Dark Mode
info.plist文件中,添加'NSRequiresAquaSystemAppearance'设置Boolean型为YES

为'info.plist'文件添加NSRequiresAquaSystemAppearance项,并选择为YES 对'info.plist'文件的NSRequiresAquaSystemAppearance项设为YES后

此时运行App后,界面是以'浅色'模式展示~

但此时代码上,仍然可以设置为'深色'模式'浅色'模式(通过设置NSApplication .shared.appearanceNSApp.appearance属性



[F].监听 '浅色'模式和'深色'模式切换:(通过NSApp来监听)

如果应用程序有不属于NSView的代码,并且不能使用上面列表列出的首选方法,它可以观察 应用程序effecveappearance属性并手动更新currentAppearance

var observation: NSKeyValueObservation?//观察的属性

监听代码

func applicationDidFinishLaunching(_ aNotification: Notification) {
    // Insert code here to initialize your application
    
    observation = NSApp.observe(\.effectiveAppearance) { (app, _) in
        //print("app.effectiveAppearance:\(app.effectiveAppearance.name)")//NSAppearanceName(_rawValue: NSAppearanceNameDarkAqua)
        //print("app.effectiveAppearance:\(app.effectiveAppearance.name.rawValue)")//NSAppearanceNameDarkAqua
        //监听到的当前模式——进行相应操作
        if app.effectiveAppearance.name == NSAppearance.Name.aqua {
            //'浅色'模式——响应的操作
            
        } else if app.effectiveAppearance.name == NSAppearance.Name.darkAqua {
            //'深色'模式——响应的操作
            
        }
        
        if #available(OSX 11.0, *) {//macOS 11.0以上才支持
            app.effectiveAppearance.performAsCurrentDrawingAppearance {
                // Invoke your non-view code that needs to be aware of the
                // change in appearance.
                //监听到的当前模式——进行相应操作
                
                
            }
        }
    }
}

监听到的当前模式('浅色'模式或'深色'模式)——再进行相应操作



[G].注意





参考文章

Supporting Dark Mode in Your Interface:https://developer.apple.com/documentation/uikit/appearance_customization/supporting_dark_mode_in_your_interface
Choosing a Specific Appearance for Your macOS App:https://developer.apple.com/documentation/appkit/nsappearancecustomization/choosing_a_specific_appearance_for_your_macos_app

stackoverflow | How can dark mode be detected on macOS 10.14?









goyohol's essay

上一篇 下一篇

猜你喜欢

热点阅读