为什么NSColor.controlTextColor会根据背景颜色变化?

Why does NSColor.controlTextColor change according to background color?

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

的颜色

比如我设置背景色为白色时,字体就变成了黑色

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

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

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

看看这个简洁的扩展程序:

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 subclass 达到与 .controlTextColor 相同的效果。

这是一个子classed 元素的示例,它使用其 backgroundColor 来确定 textColor,但是,实现相同的效果。根据您向 class 应用的 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