Как определить смену режима Свет / Темнота в iOS 13?

Некоторые настройки пользовательского интерфейса, не работающие автоматически с темным / светлым режимом, меняются как UIColor. Напримерshadowв слое. Поскольку мне нужно удалить и отбросить тень в темном и светлом режиме, мне нужно куда-то положитьupdateShadowIfNeeded()функция. Я знаю, как определить текущий режим:

func dropShadowIfNeeded() {
    switch traitCollection.userInterfaceStyle {
    case .dark: removeShadow()
    case .light: dropShadowIfNotDroppedYet()
    default: assertionFailure("Unknown userInterfaceStyle")
    }
}

Теперь я помещаю функцию в layoutSubviews, поскольку он вызывается каждый раз при изменении внешнего вида:

override func layoutSubviews() {
    super.layoutSubviews()
    dropShadowIfNeeded()
}

Но эта функция называется МНОГО. Какую функцию следует запускать, только еслиuserInterfaceStyle изменилось?

3 ответа

Решение

SwiftUI

С простой переменной окружения на \.colorScheme ключ:

struct ContentView: View {
    @Environment(\.colorScheme) var colorScheme

    var body: some View {
        Text(colorScheme == .dark ? "Its Dark" : "Its. not dark! (Light)")
    }
}

UIKit

Как описано в WWDC 2019 - Сессия 214 около 23:30.

Как я и ожидал, эта функция часто вызывается, в том числе при изменении цвета. Наряду со многими другими функциями дляViewController а также presentationController. Но есть специальная функция, которая имеет схожую сигнатуру во всехView представители.

Взгляните на это изображение из этого сеанса:

Серый: звонит, но не подходит для моей проблемы, Зеленый: предназначен для этого

Поэтому я должен вызвать его и проверить внутри этой функции:

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)
    
    if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
        dropShadowIfNeeded()
    }
}

Это гарантирует, что будет вызываться только один раз за изменение.

если вы ищете только начальное состояние стиля, посмотрите этот ответ здесь

Я думаю, что это должно вызываться значительно реже, плюс охранник следит за тем, чтобы вы реагировали только на изменения стиля пользовательского интерфейса:

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)

    guard previousTraitCollection?.userInterfaceStyle != traitCollection.userInterfaceStyle else {
        return
    }
    dropShadowIfNeeded()
}

С помощью среды выполнения RxSwift и ObjectiveC вы можете добиться этого без наследования.

вот инкапсулированная версия:

import UIKit
import RxSwift
import RxCocoa

enum SystemTheme {
    static func get(on view: UIView) -> UIUserInterfaceStyle {
        view.traitCollection.userInterfaceStyle
    }

    static func observe(on view: UIView) -> Observable<UIUserInterfaceStyle> {
        view.rx.methodInvoked(#selector(UIView.traitCollectionDidChange(_:)))
            .map { _ in SystemTheme.get(on: view) }
            .distinctUntilChanged()
    }
}
Другие вопросы по тегам