I am using quite often semantic colors in the following way to have dynamic colors for dark mode and light mode. With this approach the colors will also update on runtime when the user switches dark / light mode:
public static var bw100: UIColor = {
if #available(iOS 13, *) {
return UIColor { (UITraitCollection: UITraitCollection) -> UIColor in
if UITraitCollection.userInterfaceStyle == .dark {
// Return the color for Dark Mode
return .black
} else {
// Return the color for Light Mode
return .white
}
}
} else {
// Return a fallback color for iOS 12 and lower.
return .white
}
}()
Now I want to do the same with a Float
value, like having a semantic float var. That means I can access a different float value for dark mode and for light mode AND the value will adapt on runtime if the user switches dark / light mode. I couldn't find a solution for this.
This does NOT work, as it does not update on runtime. The app has to be restarted after dark / light mode switch:
public static var myFloat: Float = {
if #available(iOS 13.0, *) {
if UITraitCollection.current.userInterfaceStyle == .dark {
return 0.9
}
else {
return 0.1
}
}
return 0.1
}()
This does also NOT work (tried similar approach to the working one above), but here I get an error Initializer init(_:) requires that (UITraitCollection) -> Float conforms to BinaryInteger
public static var myFloat: Float = {
if #available(iOS 13, *) {
return Float { (UITraitCollection: UITraitCollection) -> Float in
if UITraitCollection.userInterfaceStyle == .dark {
// Return the Float for Dark Mode
return 0.9
} else {
// Return the Float for Light Mode
return 0.1
}
}
} else {
// Return a fallback for iOS 12 and lower.
return 0.1
}
}()
You cannot achieve something identical to how UIColor
works with a Float
as UIColor
has a special initializer that directly to interface style change. However the solution is still fairly simple, as you have to, as mentioned, listen to an interface style change by implementing traitCollectionDidChange(_:)
and recompute your data by hand.
The following code should achieve it for you:
// ViewController.swift
var myStoredFloat: Float = 1.0 {
willSet {
print(newValue)
}
}
var myComputedFloat: Float {
let tc = UITraitCollection.current
let mode = tc.userInterfaceStyle
if #available(iOS 13.0, *) {
return (mode == .light ? 1 : 0)
}
return 1
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
self.myStoredFloat = self.myComputedFloat
}
Of course you can get rid of the stored property completely if you don't depend on it and just use the computed property.
*Thanks to matt for the computed property code.