Thanks Kurt Revis for pointing my to this. I was able to do this using a special UIColor initializer that Swift provides. The only downside is that this will not work in interface builder (since it is not baked into the Asset itself), but in code this will work perfectly:
class ElevatedColor: UIColor {
convenience init(regular: UIColor, elevated: UIColor) {
if #available(iOS 13.0, *) {
self.init { (traitCollection: UITraitCollection) -> UIColor in
let isElevated = (traitCollection.userInterfaceLevel == .elevated)
return isElevated ? elevated : regular
}
} else {
self.init(cgColor: regular.cgColor)
}
}
}
This uses UIColor.init(dynamicProvider:)
. iOS will call the provided block whenever the interface traits change, so the color automatically updates when switching to an elevated context. Unfortunately, because of the way UIColor works, you can only make the initializer a convenience initializer, so technically you could create an ElevatedColor without an elevated version, but for me this is acceptable.