Dynamic colors
Dynamic colors are changing its content depending on the application appearance mode (Any (unspecified), Light or Dark). The API is available from iOS 13.
Apple provides colors out-of-the-box that are handy to use with default UI elements.
Color | Area of usability | color API |
---|---|---|
Label | text with a primary content | label |
Secondary label | text with a secondary content | secondaryLabel |
Link | link colors | link |
There are many colors with own system coding for labels, background, separators covering all basic UI needs but sometimes a developer needs to define a custom one. It can be done via Xcode assets:
Or with UIColor dynamic provider:
var dynamicColor = UIColor.init { (traitCollection) -> UIColor in
return traitCollection.userInterfaceStyle == .dark ? .white : .black
}
We can derive userInterfaceStyle: UIUserInterfaceStyle
(any, light, dark) from traitCollection: UITraitCollection
.
UITraitCollection
describes the interface environment with a collection of traits (size classes, display scale, user interface style, user interface idiom, preferred content size etc).
From iOS 13 the trait collection has a UITraitCollection.current
class member which defines a current UI environment. It's a thread-local variable so it's safe and fast-enough to use on any thread.
The dynamic color can be resolved with a specific trait collection.
dynamicColor.resolvedColor(with: UITraitCollection.current)
It's important to note that Core Animation objects: CALayer , CATextLayer , CAGradientLayer etc, NSAttributedText and drawRect:
would not reflect change of the current trait collection and it's needed to be updated explicitly.
Each layer in a view hierarchy has adopted protocol UITraitEnvironment
that presents UITraitCollection for: UIScreen, UIWindow, UIViewController, UIPresentationController, UIView.
Each layer can have overwritten property preferredUserInterfaceStyle: UIUserInterfaceStyle
and a listener of UITraitEnvironment updates:
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
// Resolve dynamic colors
// Apply colors for Core Animation objects
}
}
It listens for a change of collection and gives a previous trait collection value to compare with and filter out only changes related to colors because there can be any trait change.