6

I have a bunch of colors that I'm updating to support the new iOS 13 dark mode. I'm using them programmatically, with the new dynamic provider block constructor. The problem here is that sometimes the block is called with the wrong value for the userInterfaceStyle property, causing the color to return the wrong value (sometimes dark instead of light, sometimes light instead of dark).

From my understanding, I don't need to listen for traitCollection changes or anything of the sorts with this new constructor. It should do it automatically (or am I wrong?).

The funny thing here is that I tried doing it using some dummy colors through the assets catalog and it also does not work. Also tried using some of the new system provided dynamic colors, like .systemBackgroundColor. It also resolves the color wrong sometimes.

if #available(iOS 13.0, *) {
    return UIColor { (traitCollection: UITraitCollection) -> UIColor in
        if traitCollection.userInterfaceStyle == .dark {
            return darkColor
        }
        else {
            return lightColor
        }
    }
}

So, what exactly is supposed to happen? Should this work as I'm expecting or do I absolutely need to assign the colors in a specific place (like inside viewWillLayoutSubviews or traitCollectionDidChange)?

Resolving the color manually, using .resolvedColor(with: UITraitCollection) works. But from what I understood from the documentation, I should not need to resolve it manually like this.

Yuri Reis
  • 131
  • 6
  • 2
    This all indicates that the object you are assigning the color _to_ is not responding to trait environment changes. This is for instance the case for all `CALayer` properties. Where are you using that color? – Frank Rupprecht Sep 05 '19 at 09:09
  • @FrankSchlegel I'm using it on the `navigationBar.barTintColor`, `navigationBar.tintColor`, searchBar `searchTextField.backgroundColor`. Just to name a few. They are all UIColor properties from UIView subclasses, so it should work, no? Also, using it inside `NSForegroundColorAttributeName` for attributed strings. – Yuri Reis Sep 05 '19 at 12:58
  • Are you actually seeing an incorrectly resolved color in your navigation bar, search field, etc? Or is the problem just that your block is called sometimes with a trait collection that you don't expect? – Kurt Revis Sep 06 '19 at 06:41
  • 2
    @KurtRevis It actually resolves the wrong color. The navigationBar displays the dark color when it should display the light one. And like I said in the main post, if I use `.resolvedColor(with: UITraitCollection)` it works perfectly fine. So it's a problem with the automatic behavior. – Yuri Reis Sep 06 '19 at 12:23
  • @FrankSchlegel so what should be done in case of `CALayer` properties? I'm having this same issue when setting `borderColor` of some button. – tadija Apr 07 '20 at 14:48
  • 1
    @tadija You have to override `traitCollectionDidChange` in the view or view controller that handles the layer, check if it `hasDifferentColorAppearance(comparedTo: previousTraitCollection)` (optional) and re-assign the `borderColor` in this case. – Frank Rupprecht Apr 08 '20 at 05:48

1 Answers1

7

Ok, so I found the source of the problem. When setting dynamic colors to a navigationBar or to a searchBar, pay attention to the barStyle attribute of the view. I was using .dark for the navigationBar (to display the status bar with white text). That was causing the UIColor dynamic provider to resolve the wrong color. Changing it to .default fixed it for me.

For the searchBar, I wasn't setting anything specific. Forcing it to .default also fixed it.

Yuri Reis
  • 131
  • 6
  • But how do you keep the white text status bar? I have the same problem but when setting the `barStyle` of `navigationBar` to default the status bar text changes to black what I don't want. Haven't found a solution yet. – PatrickDotStar Jun 05 '20 at 09:52
  • You can override the `preferredStatusBarStyle` of your controller to be `.lightContent` for that case. It shouldn't impact your `navigationBar` style. – Yuri Reis Jul 29 '21 at 15:41