UIColor возвращает неверные значения для цветов темного режима

У меня есть собственный подкласс UITextField, который меняет цвет границы при вводе в него чего-либо. Я слушаю изменения, звоня

self.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)

а затем в textFieldDidChange(_:) я делаю:

self.layer.borderColor = UIColor(named: "testColor")?.cgColor

Где testColor — цвет, определенный в Assets.xcassets с вариантами для светлого и темного режима. Проблема в том, что UIColor(named: "testColor")?.cgColor всегда возвращает цвет для светлого режима.

Это ошибка бета-версии iOS 13 или я что-то не так делаю? Есть репозиторий GitHub с кодом, демонстрирующим такое поведение. Запустите проект, переключитесь в темный режим из XCode, затем начните вводить что-то в текстовое поле.

вы пытались запустить это на устройстве и сделать это?

Lu_ 23.07.2019 15:28

Да, он делает то же самое.

ov1d1u 23.07.2019 15:29
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
28
2
8 227
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Краткий ответ

В этой ситуации вам нужно указать, какую коллекцию признаков использовать для разрешения динамического цвета.

self.traitCollection.performAsCurrent {
    self.layer.borderColor = UIColor(named: "testColor")?.cgColor
}

или

self.layer.borderColor = UIColor(named: "testColor")?.resolvedColor(with: self.traitCollection).cgColor

Более длинный ответ

Когда вы вызываете метод cgColor для динамического UIColor, ему необходимо разрешить значение динамического цвета. Это делается путем обращения к текущему набору черт, UITraitCollection.current.

Текущая коллекция черт устанавливается UIKit, когда он вызывает ваши переопределения определенных методов, а именно:

  • UIView
    • рисовать()
    • layoutSubviews()
    • traitCollectionDidChange()
    • tintColorDidChange()
  • UIViewController
    • видWillLayoutSubviews()
    • viewDidLayoutSubviews()
    • traitCollectionDidChange()
  • UIPresentationController
    • контейнерViewWillLayoutSubviews()
    • containerViewDidLayoutSubviews()
    • traitCollectionDidChange()

Однако, помимо переопределений этих методов, текущая коллекция трейтов не обязательно имеет какое-либо конкретное значение. Итак, если ваш код не переопределяет один из этих методов, и вы хотите разрешить динамический цвет, вы должны сообщить нам, какую коллекцию свойств использовать.

(Это потому, что можно переопределить признак userInterfaceStyle любого представления или контроллера представления, поэтому, даже если устройство может быть установлено в светлом режиме, у вас может быть представление в темном режиме.)

Вы можете сделать это, напрямую разрешив динамический цвет, используя метод UIColor resolvedColor(with:). Или используйте метод performAsCurrent UITraitCollection и поместите свой код, который разрешает цвет внутри замыкания. Краткий ответ выше показывает оба пути.

Вы также можете переместить свой код в один из этих методов. В этом случае, я думаю, вы могли бы поставить его в layoutSubviews(). Если вы это сделаете, он будет автоматически вызываться при изменении светлого/темного стиля, поэтому вам больше ничего не нужно делать.

Справка

WWDC 2019, Реализация темного режима в iOS

Начиная с 19:00 я говорил о том, как разрешаются динамические цвета, а в 23:30 я представил пример того, как установить динамический цвет границы CALayer, как вы делаете.

Я отмечаю этот ответ как принятый, так как использование предложенного кода работает, даже когда я перехожу в темный режим с помощью кнопки Environment Overrides в Xcode. Спасибо, что разъяснили, как работает динамический UIColor.

ov1d1u 24.07.2019 10:02

У меня проблема, аналогичная OP, когда я устанавливаю именованный цвет (созданный в .xassets с цветами, установленными на любой и темный внешний вид) для границы некоторых кнопок, он работает непоследовательно, некоторые кнопки отображаются со светлым цветом, а некоторые с темным цвет. Другая проблема, связанная с layer.borderColor и темным режимом, заключается в том, что когда я переключаюсь со светлого на темный режим (и наоборот) в настройках iOS, он обновляет только цвет границы кнопок (даже когда он отображает правильный внешний вид цвета). ), если я перерисую кнопки (воткну vc и снова нажму). Наверное баг? (проверено в xcode 11 b6)

Lee Andrew 23.08.2019 17:29

Кстати, непоследовательное поведение, описанное выше, происходит в iOS 12 (что странно, потому что некоторые границы отображаются с темным внешним видом, хотя в iOS 12 нет поддержки темного режима), и необходимость перерисовки для получения правильного цвета после изменения режимов происходит в iOS 13b

Lee Andrew 23.08.2019 17:40

@ LeeAndrew, ты нашел исправление?

Zac 05.10.2019 11:00

@airowe Я тоже. Это сработало для меня: stackoverflow.com/questions/58312095/…

rs7 05.01.2020 20:05

@rs7 Понял, спасибо! Это сработало для меня. Вот прямая ссылка на ответ: stackoverflow.com/a/58312205/1496693

airowe 14.01.2020 20:52

@ Курт Ревис, есть ли аналогичный ответ для macOS? Я не мог найти его.

Patrick 25.02.2020 14:21

У меня были проблемы с некоторыми UIView подклассами, которые не учитывали варианты темного режима моих цветов XCAsset, но: UIColor(named: "testColor")?.resolvedColor(with: self.traitCollection) это исправлено. Спасибо!

Alex Scanlan 03.04.2020 12:01

Это не работает. UIColor.text всегда возвращает черный цвет в темном режиме, что делает его нечитаемым.

inexcitus 07.01.2022 06:10

Вы можете добавить невидимое подпредставление в ваше представление, и оно будет отслеживать события traitCollectionDidChange.

пример:

import UIKit

extension UIButton {
    func secondaryStyle() {
        backgroundColor = .clear
        layer.cornerRadius = 8
        layer.borderWidth = 1
        layer.borderColor = UIColor(named: "borderColor")?.cgColor

        moon.updateStyle = { [weak self] in
            self?.secondaryStyle()
        }
    }
}

extension UIView {
    var moon: Moon {
        (viewWithTag(Moon.tag) as? Moon) ?? .make(on: self)
    }

    final class Moon: UIView {
        static var tag: Int = .max - 1

        var updateStyle: (() -> Void)?

        static func make(on view: UIView) -> Moon {
            let moon = Moon()
            moon.tag = Moon.tag
            view.addSubview(moon)
            return moon
        }

        override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
            super.traitCollectionDidChange(previousTraitCollection)
            guard previousTraitCollection?.userInterfaceStyle != traitCollection.userInterfaceStyle else { return }
            triggerUpdateStyleEvent()
        }

        func triggerUpdateStyleEvent() {
            updateStyle?()
        }
    }
}

Другие вопросы по теме