【问题标题】:Why does NSColor.controlTextColor change according to background color?为什么 NSColor.controlTextColor 会根据背景颜色而变化?
【发布时间】:2022-01-15 23:55:15
【问题描述】:

我在做一个Cocoa应用,发现只要将NSTextField的字体颜色设置为NSColor.controlTextColor,字体就会根据NSTextField的背景颜色变化。

例如,当我将背景颜色设置为白色时,字体变为黑色。

但是当我将背景颜色设置为黑色时,字体变为白色。

我想定义一个NSColor 来达到同样的效果。如何实现?

【问题讨论】:

  • 在 NSView(子)类的绘制方法中实现的层次比较低。如果您在谈论 Mojave 中的暗模式,您可以在图像资源中为明暗定义不同的颜色。
  • labelColor 和许多其他系统颜色现在是 动态 颜色;它们根据视图/窗口/屏幕/系统的外观模式而变化。我怀疑您必须创建自己的 NSColor 子类才能达到类似的效果,我敢说这将是一项不平凡的任务。
  • @JamesBucanek 是的,这对我来说完全是一个毫无头绪的工作。所以我想看看有没有其他人对这份工作有所了解并能给我一些帮助。
  • 您是否只是想更改 macOS 的深色与浅色模式的颜色?或者您是否试图确定给定特定的背景颜色是否应该显示不同的颜色?前者容易,后者则不那么容易。

标签: swift macos cocoa nscolor


【解决方案1】:

如果您想传入任何颜色,然后确定哪种文本颜色更理想 - 黑色或白色 - 您首先需要确定该颜色的 亮度(以 sRGB 为单位)。我们可以通过转换为灰度,然后检查黑白对比来做到这一点。

看看这个简洁的扩展:

extension NSColor {

    /// Determine the sRGB luminance value by converting to grayscale. Returns a floating point value between 0 (black) and 1 (white).
    func luminance() -> CGFloat {
        var colors: [CGFloat] = [redComponent, greenComponent, blueComponent].map({ value in
            if value <= 0.03928 {
                return value / 12.92
            } else {
                return pow((value + 0.055) / 1.055, 2.4)
            }
        })
        let red = colors[0] * 0.2126
        let green = colors[1] * 0.7152
        let blue = colors[2] * 0.0722
        return red + green + blue
    }

    func contrast(with color: NSColor) -> CGFloat {
        return (self.luminance() + 0.05) / (color.luminance() + 0.05)
    }

}

现在我们可以通过检查背景颜色与黑色之间的对比度并将其与白色的对比度进行比较来确定我们应该使用黑色还是白色作为文本。

// Background color for whatever UI component you want.
let backgroundColor = NSColor(red: 0.5, green: 0.8, blue: 0.2, alpha: 1.0)
// Contrast of that color w/ black.
let blackContrast = backgroundColor.contrast(with: NSColor.black.usingColorSpace(NSColorSpace.sRGB)!)
// Contrast of that color with white.
let whiteContrast = backgroundColor.contrast(with: NSColor.white.usingColorSpace(NSColorSpace.sRGB)!)
// Ideal color of the text, based on which has the greater contrast.
let textColor: NSColor = blackContrast > whiteContrast ? .black : .white

在上面的这种情况下,backgroundColor 产生10.595052467245562 与黑色的对比,0.5045263079640744 与白色的对比。很明显,我们应该使用黑色作为我们的字体颜色!

黑色的值可以证实here


编辑:.controlTextColor 的逻辑将隐藏在 Apple 提供的 API 的表面之下,并且超出了我的范围。它与用户的偏好等有关,并且可能在运行时对视图进行操作(即,通过设置.controlTextColor,您可能正在标记视图以检查哪个textColor 在运行时更清晰并应用它)。

TL;DR:我认为您无法通过 NSColor 子类实现与 .controlTextColor 相同的效果。

这是一个子类元素的示例,它使用其backgroundColor 来确定@​​987654333@,但是,以实现相同的效果。根据您向班级申请的backgroundColortextColor 将由它决定。

class ContrastTextField: NSTextField {

    override var textColor: NSColor? {
        set {}
        get {
            if let background = self.layer?.backgroundColor {
                let color = NSColor(cgColor: background)!.usingColorSpace(NSColorSpace.sRGB)!
                let blackContrast = color.contrast(with: NSColor.black.usingColorSpace(NSColorSpace.sRGB)!)
                let whiteContrast = color.contrast(with: NSColor.white.usingColorSpace(NSColorSpace.sRGB)!)
                return blackContrast > whiteContrast ? .black : .white
            }
            return NSColor.black
        }
    }

}

然后你可以用:

let textField = ContrastTextField()
textField.wantsLayer = true
textField.layer?.backgroundColor = NSColor.red.cgColor
textField.stringValue = "test"

将根据layer 的背景设置您的textColor

【讨论】:

  • 感谢您的解释。也许我没有说清楚。其实我想问的是如何让我的NSColor继承类实现这样的颜色适配。也就是说,我想了解 NSColor 是如何工作的。
  • 您想要一个 NSColor 子类来完成上述操作?你到底想达到什么目的?
  • 是的,我正在实现 NSColor 的一个子类。正如我之前所说,我想减少我编写的代码量,并在控件失去焦点或获得焦点时应用我想要显示的颜色。就像图片一样。
  • 在这种情况下,您似乎想要对控件本身进行子类化。 NSColor 对象只会是您定义它们的颜色。
  • 用最好的解决方案编辑了上面的答案,恕我直言
猜你喜欢
  • 2021-02-13
  • 2020-11-01
  • 2015-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-19
  • 1970-01-01
  • 2018-05-30
相关资源
最近更新 更多