Working on support for dark mode I've been able to have dynamic colors that change as you change themes using this pattern:
static var dynamicColor: UIColor = {
if #available(iOS 13, *) {
return UIColor { (traitCollection: UITraitCollection) -> UIColor in
switch (traitCollection.userInterfaceStyle, traitCollection.accessibilityContrast) {
case (.dark, .high):
return .somethingCustom4
case (.dark, _):
return .somethingCustom2
case (_, .high):
return .somethingCustom3
default:
return .somethingCustom1
}
}
} else {
return .somethingCustom1
}
}()
I'm trying to simplify the creation of UIColor
between iOS 12 and 13 with support for dark mode. I came up with a simple Theme
enum to encapsulate all the logic above and it just exposes 4 cases, so the above declaration simplifies into this:
static var customColor: UIColor = {
switch UIColor.theme {
case .light:
return somethingCustom1
case .dark:
return .somethingCustom2
case .lightHighContrast:
return somethingCustom3
case .darkHighContrast:
return .somethingCustom4
}
}()
The problem is that while before the simplification everything updated as it should, now the theme
isn't getting the updated value so the colors do not change. This is how I'm declaring Theme
with the init(dynamicProvider:)
initializer, but it seems like UITraitCollection.current
doesn't have the right value when you switch between dark/light mode:
public enum Theme {
case light, dark, lightHighContrast, darkHighContrast
@available(iOSApplicationExtension 13.0, *)
init(dynamicProvider: @escaping (UITraitCollection) -> Theme) {
self = dynamicProvider(UITraitCollection.current)
}
}
public extension UIColor {
static var theme: Theme {
if #available(iOS 13, *) {
return Theme { (traitCollection: UITraitCollection) -> Theme in
switch (traitCollection.userInterfaceStyle, traitCollection.accessibilityContrast) {
case (.dark, .high):
return .darkHighContrast
case (.dark, _):
return .dark
case (_, .high):
return .lightHighContrast
default:
return .light
}
}
} else {
return .light
}
}
}
What am I doing wrong here? I think that's how that initializer is supposed to work but I haven't been able to find any other examples of this online…
Okay, so after a lot of messing around I'm still not sure why the original implementation doesn't work, but I did find another way of making it work! So if you're trying to simplify your implementation for support for both iOS 12 and 13, here's what I ended up with:
public extension UIColor {
private static func make(dynamicProvider: @escaping (Theme) -> UIColor) -> UIColor {
guard #available(iOSApplicationExtension 13.0, *) else { return dynamicProvider(.light) }
return UIColor { (traitCollection) -> UIColor in
return dynamicProvider(Theme(traitCollection))
}
}
static var customColor: UIColor {
return .make { (theme) -> UIColor in
switch theme {
case .light:
return .somethingCustom1
case .dark:
return .somethingCustom2
case .lightHighContrast:
return .somethingCustom3
case .darkHighContrast:
return .somethingCustom4
}
}
}
}
public enum Theme {
case light, dark, lightHighContrast, darkHighContrast
@available(iOSApplicationExtension 13.0, *)
init(_ traitCollection: UITraitCollection) {
switch (traitCollection.userInterfaceStyle, traitCollection.accessibilityContrast) {
case (.dark, .high):
self = .darkHighContrast
case (.dark, _):
self = .dark
case (_, .high):
self = .lightHighContrast
default:
self = .light
}
}
}