27

我有一个自定义UITextField子类,在其中输入内容时会更改其边框颜色。我正在通过调用来监听变化

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

然后,textFieldDidChange(_:)我正在做:

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

Assets.xcassets 中定义的颜色在哪里testColor,其中包含明暗模式的变体。问题是它UIColor(named: "testColor")?.cgColor似乎总是返回灯光模式的颜色。

这是 iOS 13 测试版中的错误还是我做错了什么?有一个 GitHub 存储库,其中包含展示此行为的代码。运行项目,从 XCode 切换到暗模式,然后开始在文本字段中输入内容。

4

2 回答 2

76

简短的回答

在这种情况下,您需要指定使用哪个特征集合来解析动态颜色。

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

或者

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

更长的答案

当您cgColor在 dynamic 上调用该方法时UIColor,它需要解析动态颜色的值。这是通过引用当前特征集合来完成的,UITraitCollection.current.

当前的特征集合是由 UIKit 在调用某些方法的覆盖时设置的,特别是:

  • 界面视图
    • 画()
    • 布局子视图()
    • traitCollectionDidChange()
    • tintColorDidChange()
  • UIViewController
    • viewWillLayoutSubviews()
    • viewDidLayoutSubviews()
    • traitCollectionDidChange()
  • UIPresentationController
    • containerViewWillLayoutSubviews()
    • containerViewDidLayoutSubviews()
    • traitCollectionDidChange()

但是,在这些方法的覆盖之外,当前特征集合不一定设置为任何特定值。因此,如果您的代码没有覆盖这些方法之一,并且您想要解析动态颜色,那么您有责任告诉我们要使用什么特征集合。

(这是因为可以覆盖userInterfaceStyle任何视图或视图控制器的特征,因此即使设备可能设置为亮模式,您也可能拥有处于暗模式的视图。)

您可以通过使用 UIColor 方法直接解析动态颜色来做到这一点resolvedColor(with:)。或者使用 UITraitCollection 方法performAsCurrent,并将解析颜色的代码放在闭包内。上面的简短回答显示了两种方式。

您还可以将您的代码移动到其中一种方法中。在这种情况下,我认为您可以将其放入layoutSubviews(). 如果你这样做,它会在明暗风格改变时自动被调用,所以你不需要做任何其他事情。

参考

WWDC 2019,在 iOS 中实现暗模式

Starting at 19:00 I talked about how dynamic colors get resolved, and at 23:30 I presented an example of how to set a CALayer's border color to a dynamic color, just like you're doing.

于 2019-07-24T07:25:31.933 回答
0

You can add invisible subview on you view and it will track traitCollectionDidChange events.

example:

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?()
        }
    }
}
于 2021-05-12T09:44:15.867 回答