如何在 iOS 13 中检测 Light\Dark 模式变化?

How to detect Light\Dark mode change in iOS 13?

某些 UI 设置不会自动与 Dark/Light 模式一起工作,而更改为 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 更改时才触发的正确函数是什么?

我认为调用它的频率应该大大降低,而且守卫确保您只对用户界面样式更改做出反应:

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

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

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 - Session 214 中描述的那样 23:30。

如我所料,这个函数被调用了很多次,包括当颜色改变时。除了 ViewControllerpresentationController 的许多其他功能。但是有一些特殊的功能设计用于在所有 View 代表中具有相似的签名。

看看这张会话中的图片:

灰色:呼吁但对我的问题不利,绿色:为此设计

所以我应该调用它并在这个函数中检查它:

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

这将保证每次更改只调用一次。

如果您只是寻找样式的初始状态,check out this answer here

借助RxSwift和ObjectiveC runtime,无需继承即可实现

这里是封装版本:

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()
    }
}