【问题标题】:UIColor return wrong values for dark mode colorsUIColor 为深色模式颜色返回错误值
【发布时间】:2019-12-01 13:32:07
【问题描述】:

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

【问题讨论】:

  • 您是否尝试在设备上运行它并这样做?
  • 是的,也是一样的。

标签: ios swift uitextfield ios13 ios-darkmode


【解决方案1】:

简答

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

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

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

更长的答案

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

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

  • UI 视图
    • 画()
    • 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 的边框颜色设置为动态颜色的示例,就像您正在做的那样。

【讨论】:

  • 我将此答案标记为已接受的答案,因为即使我从 Xcode 中的“环境覆盖”按钮更改为暗模式,使用建议的代码也有效。感谢您阐明动态 UIColor 的工作原理。
  • 我有一个与 OP 类似的问题,当我将命名颜色(在 .xassets 中创建,颜色设置为 Any 和 Dark 外观)设置为某些按钮的边框时,它的工作方式不一致,一些按钮出现浅色和一些深色。关于 layer.borderColor 和 Dark Mode 的另一个问题是,当我在 iOS 设置中从 Light 模式更改为 Dark 模式(反之亦然)时,它只会更新按钮边框的颜色(即使它显示正确的颜色外观) ) 如果我重绘按钮(弹出 vc 并再次按下)。应该是bug吧? (在 xcode 11 b6 中测试)
  • 顺便说一句,我上面描述的不一致行为发生在 iOS 12 上(这很奇怪,因为即使 iOS 12 不支持深色模式,某些边框也会出现深色外观)并且需要重绘才能获得更改模式后的正确颜色发生在 iOS 13b 中
  • @LeeAndrew 你找到解决办法了吗?
  • @airowe 我也没有。不过这对我有用:stackoverflow.com/questions/58312095/…
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-04-06
    • 1970-01-01
    • 1970-01-01
    • 2017-08-11
    • 1970-01-01
    • 2020-11-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多