Почему NSColor.controlTextColor изменяется в зависимости от цвета фона?

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

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

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

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

введите здесь описание изображения  введите описание изображения здесь


person Simon    schedule 08.05.2019    source источник
comment
Это реализовано на довольно низком уровне в методах рисования (подклассов) NSView. Если вы говорите о темном режиме в Мохаве, вы можете определить разные цвета для светлых и темных в изображениях.   -  person vadian    schedule 08.05.2019
comment
labelColor и многие другие системные цвета теперь являются динамическими цветами; они меняются в зависимости от режима отображения вида / окна / экрана / системы. Я подозреваю, что вам придется создать свой собственный подкласс NSColor для достижения аналогичного эффекта, и я рискну предположить, что это будет нетривиальная задача.   -  person James Bucanek    schedule 09.05.2019
comment
@JamesBucanek Да, для меня это совершенно бессмысленная работа. Поэтому я хочу узнать, знает ли кто-нибудь еще что-нибудь об этой работе и может ли мне помочь.   -  person Simon    schedule 09.05.2019
comment
Вы просто хотите изменить цвет для режимов MacOS Dark vs Light? Или вы пытаетесь определить с учетом определенного цвета фона, следует ли отображать другой цвет? Первое - это просто, второе - не очень.   -  person Lucas Derraugh    schedule 17.05.2019


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

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


РЕДАКТИРОВАТЬ: Логика для .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.

person jake    schedule 09.06.2019
comment
Спасибо за ваше объяснение. Может, я не прояснил. Фактически, я хотел спросить, как заставить мои классы наследования NSColor достичь такой цветовой адаптации. То есть я хотел понять, как работает NSColor. - person Simon; 10.06.2019
comment
Вам нужен подкласс NSColor, который выполняет вышеуказанное? Чего именно вы пытаетесь достичь? - person jake; 10.06.2019
comment
Да, я реализую подкласс NSColor. Как я сказал ранее, я хочу уменьшить объем кода, который я кодирую, и применить цвет, который я хочу отображать, когда элемент управления теряет фокус или получает фокус. Прямо как на картинке. - person Simon; 11.06.2019
comment
В этом случае кажется, что вы хотите создать подкласс самого элемента управления. Объекты NSColor будут иметь только тот цвет, который вы их определили. - person jake; 11.06.2019
comment
Отредактировал ответ выше наилучшим возможным решением, imho - person jake; 11.06.2019