UIColor返回深色模式颜色的错误值

时间:2019-07-23 13:26:43

标签: ios swift uitextfield ios13 ios-darkmode

我有一个自定义的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 beta中的错误,还是我做错了什么?带有a GitHub repo的代码可以展示这种行为。运行项目,从XCode切换到暗模式,然后开始在文本字段中输入内容。

3 个答案:

答案 0 :(得分: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
    • draw()
    • layoutSubviews()
    • traitCollectionDidChange()
    • tintColorDidChange()
  • UIViewController
    • viewWillLayoutSubviews()
    • viewDidLayoutSubviews()
    • traitCollectionDidChange()
  • UIPresentationController
    • containerViewWillLayoutSubviews()
    • containerViewDidLayoutSubviews()
    • traitCollectionDidChange()

但是,在这些方法的覆盖范围之外,当前特征集不必设置为任何特定值。因此,如果您的代码未覆盖这些方法之一,而您想解析一种动态颜色,则有责任告诉我们要使用的特征集。

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

您可以通过使用UIColor方法resolvedColor(with:)直接解析动态颜色来实现。或使用UITraitCollection方法performAsCurrent,然后将用于解析颜色的代码放入闭包中。上面的简短答案显示了两种方式。

您还可以将代码移入这些方法之一。在这种情况下,我认为您可以将其放在layoutSubviews()中。如果这样做,则当明暗样式更改时,它将自动被调用,因此您无需执行其他任何操作。

参考

WWDC 2019, Implementing Dark Mode in iOS

从19:00开始,我谈到了动态色彩的分辨方式,在23:30,我举了一个示例,说明如何将CALayer的边框颜色设置为动态颜色,就像您做。

答案 1 :(得分:0)

由于我刚刚发现问题所在,因此我将其发布为答复。如果我从Simulator-> Settings-> Developer激活Dark Appearance,这似乎是一个错误,UITextField具有预期的颜色。仅当我从Xcode中的“环境覆盖”按钮将界面样式设置为深色时,才会出现该问题。

答案 2 :(得分:0)

您可以在视图上添加不可见的子视图,它会跟踪 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?()
        }
    }
}