I am doing practice with UIColor and CGColor from extension and struct. Everything work is fine, but I realized what if in future I will add "Theme" feature, if I change from Red to Blue, how can I update all UIColor and CGColor when I change the theme?
For example, when I press button to change theme to blue, how can it update all UIColor / CGColor?
Here my code:
extension UIColor {
public class func dynamicColor(light: UIColor, dark: UIColor) -> UIColor {
return UIColor {
switch $0.userInterfaceStyle {
case .dark: return dark
default: return light
}
}
}
}
var themePick = "Red"
struct AiooColor {
static var tableviewerHeaderTitle: UIColor {
switch themePick {
case "Red": return UIColor.dynamicColor(light: UIColor.systemRed, dark: UIColor.systemRed)
case "Blue": return UIColor.dynamicColor(light: UIColor.systemBlue, dark: UIColor.systemBlue)
// DEFAULT WILL SET TO RED
default: return UIColor.dynamicColor(light: UIColor.systemRed, dark: UIColor.systemRed)
}
}
static var tableviewerHeaderBackground: UIColor {
switch themePick {
case "Red": return UIColor.dynamicColor(light: UIColor(red: 255/255, green: 204/255, blue: 204/255, alpha: 1), dark: UIColor(red: 30/255, green: 0/255, blue: 0/255, alpha: 1))
case "Blue": return UIColor.dynamicColor(light: UIColor(red: 204/255, green: 204/255, blue: 255/255, alpha: 1), dark: UIColor(red: 0/255, green: 0/255, blue: 30/255, alpha: 1))
// DEFAULT WILL SET TO RED
default: return UIColor.dynamicColor(light: UIColor(red: 20/255, green: 0/255, blue: 0/255, alpha: 1), dark: UIColor(red: 20/255, green: 0/255, blue: 0/255, alpha: 1))
}
}
}
Say you have the following struct to keep track of user preferences keys:
struct PreferenceKeys {
static let themeStyle = "theme_style"
}
And the following enum (with raw type) to describe your theme style:
enum ThemeStyle: Int {
case themeA = 0
case themeB
case themeC
case themeD
}
First I'd create a custom service to manage your theme. Let's call it ThemeService. This will allow you to store and retrieve the active theme style the user picked. It could be as simple as this:
protocol ThemeServiceInterface {
func setThemeStyle(_ themeStyle: ThemeStyle)
func getThemeStyle() -> ThemeStyle
}
final class ThemeService: ThemeServiceInterface {
static let shared = ThemeService()
let userDefaults: UserDefaults
private init() {
self.userDefaults = UserDefaults.standard
}
func setThemeStyle(_ themeStyle: ThemeStyle) {
userDefaults.set(themeStyle.rawValue, forKey: PreferenceKeys.themeStyle)
}
func getThemeStyle() -> ThemeStyle {
let rawValue = userDefaults.integer(forKey: PreferenceKeys.themeStyle)
if let themeStyle = ThemeStyle(rawValue: rawValue) {
return themeStyle
}
return .themeA
}
}
So when you try to access a specific color, simply ask your Theme Service what's the active theme style, and set your color accordingly.
extension UIColor {
static var primaryColor: UIColor {
switch ThemeService.shared.getThemeStyle() {
case .themeA: return .systemBlue
case .themeB: return .systemRed
case .themeC: return .systemGreen
case .themeD: return .systemIndigo
}
}
static var secondaryColor: UIColor {
.systemPink
}
// ...
}
Now when your user wants to set a new theme style, you would just call the following method:
ThemeService.shared.setThemeStyle(.themeC)
This is great, and all your newly created views will reflect the new theme style.
But now comes the part where we need to reset the UI for all the existing views as well. Well, depending on the structure of your project, you would have different options.
For example you could make your ThemeService send a notification via NotificationCenter
to all your screens, asking them to reload their UI (using setNeedsDisplay()
/ setNeedsLayout()
methods for example)
If this sounds like too much work for your project, an other nice and easy way could be to simply reset your UIWindow's rootViewController
with a beautiful animation, which would cause your UI to be resetted and match the active theme style.
Hope it helps!