Почему NSColor.controlTextColor изменяется в зависимости от цвета фона? - PullRequest
2 голосов
/ 08 мая 2019

Я работаю над приложением какао и обнаружил, что, если цвет шрифта NSTextField установлен в NSColor.controlTextColor, шрифт будет меняться в соответствии с цветом фона NSTextField.

Например, когда я устанавливаю белый цвет фона, шрифт становится черным.

Но когда я устанавливаю цвет фонак черному шрифт становится белым.

Я хочу определить NSColor для достижения того же эффекта.Как этого добиться?

enter image description here enter image description here

1 Ответ

3 голосов
/ 09 июня 2019

Если вы хотите передать любой цвет, а затем определить, какой цвет текста будет более идеальным - черный или белый - вам сначала нужно определить яркость этого цвета (в 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 с белым.Ясно, что мы должны использовать черный в качестве цвета шрифта!

Значение черного можно подтвердить здесь .


РЕДАКТИРОВАТЬ: Логика для .controlTextColor будет лежать под поверхностью API, предоставляемого Apple, и вне меня.Он связан с предпочтениями пользователя и т. Д. И может работать с представлениями во время выполнения (т. Е. Установив .controlTextColor, вы можете пометить представление, чтобы проверить, какой из них textColor более разборчиво во время выполнения, и применить его).

TL; DR: я не думаю, что у вас есть возможность достичь того же эффекта, что и .controlTextColor с подклассом NSColor.

Вот примерподклассный элемент, который использует backgroundColor для определения textColor, однако для достижения того же эффекта.В зависимости от того, backgroundColor вы применяете к классу, textColor будет определяться им.

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"

Устанавливают ваш textColor в зависимости от фона layer.

...