Swift Extension and Enum for Color Schemes

Swift extension and enum for color schemes

Don't use an enum then. If you don't want to enumerate on the values in a switch statement, there is no need for an enum. Use a struct with constant attributes.

struct Color {
static let border = UIColor(red:0.92, green:0.93, blue:0.94, alpha:1.00)
static let waterMelon = UIColor(red:0.97, green:0.38, blue:0.45, alpha:1.00)
// and so on ....
}

If you want to extend UIColor to have access to all the other colors of UIColor as well, you can extend UIColor like this:

extension UIColor {
static var border: UIColor {
return UIColor(red:0.92, green:0.93, blue:0.94, alpha:1.00)
}

static var waterMelon: UIColor {
return UIColor(red:0.97, green:0.38, blue:0.45, alpha:1.00)
}
}

How can I make a Swift enum with UIColor value?

I do it like this (basically using a struct as a namespace):

extension UIColor {
struct MyTheme {
static var firstColor: UIColor { return UIColor(red: 1, green: 0, blue: 0, alpha: 1) }
static var secondColor: UIColor { return UIColor(red: 0, green: 1, blue: 0, alpha: 1) }
}
}

And you use it like:

UIColor.MyTheme.firstColor

So you can have a red color inside your custom theme.

Declaring an enum to change color used in app for different type of users

I believe what you want is not possible. Enum may not be a class, only a generic type, a structure.

But it is usually not what you want as when you will have many of these values you will have a mess anyway. For instance at some point you will have a button background color, border color and text color. Then waht do you expect your result to be like:

button.backgroundColor = UIButton.backgroundColor
button.layer.borderColor = UIButton.borderColor.cgColor
button.label.textColor = UIButton.textColor

And now having 3 enums for a single component... It is a solution but I think it is a bad one...

I suggest you rather create a static class and have it like so:

class UserApperance {
static var userType: UserType = .admin

static var navigationTintColor: UIColor {
switch userType {
case .admin: ...
case .user: ...
}
}

}

So all you will do in the code is again

tabBarController.tabBar.barTintColor = UserApperance.navigationTintColor

And when you want to group them you can use nesting:

class UserApperance {
static var userType: UserType = .admin

class NavigationBar {
static var tintColor: UIColor {
switch UserApperance.userType {
case .admin: ...
case .user: ...
}
}
}

}

Where the result is now nicer:

tabBarController.tabBar.barTintColor = UserApperance.NavigationBar.tintColor

In my opinion this is the nicest way and with really hard situations like white-label applications you can really play around with nesting and do it per screen and even have code like:

let appearance =  UserAppearance.thisScreenName
titleLabel.textColor = appearance.title.textColor
titleLabel.font = appearance.title.font

Bot to mention you can generalize some things and even have usage as

UserAppearance.thisScreenName.title.configure(label: titleLabel)

Which may set all the property of your UI element...

But if you really, really badly want to have this you can still use something like strings as colors or pretty much any primitive...

You can create extensions like

extension UIView {

var backgroundColorString: String? {
set {
self.backgroundColor = UIColor(hexString: newValue)
}
get {
return self.backgroundColor.toHexString()
}
}

}

Then you obviously use this property to set the color directly from enumeration. But I discourage you to do this.

SwiftUI color extension for creating theme

There are some code mistakes.

  1. You have created a function with the name colorColor but your are called a color
  2. Used wrong traitCollection

Here is the fixed code.

enum Colors {
enum Content {
static var contentStrongestColor: Color {
return setColor(
dark: Color(red: 0.21568627655506134, green: 0.2549019753932953, blue: 0.3176470696926117),
light: Color(red: 0.8784313797950745, green: 0.8784313797950745, blue: 0.8784313797950745)
)
}
}
enum Background {
static var contentDefaultColor: Color {
return setColor(
dark: Color(red: 0.06666667014360428, green: 0.09019608050584793, blue: 0.15294118225574493),
light: Color(red: 1, green: 1, blue: 1)
)
}
}
}

extension Colors {
static func setColor(dark: Color, light: Color) -> Color {
switch UIScreen.main.traitCollection.userInterfaceStyle {
case .dark: return dark
case .light: return light
default: return light
}
}
}

How to implement a color scheme switch with the system value option?

Thanks to @diogo for his solution. I have adapted it for ios 15 into a custom view which could be used in a settings page:

struct DisplayModeSetting: View {

enum DisplayMode: Int {
case system, dark, light

var colorScheme: ColorScheme? {
switch self {
case .system: return nil
case .dark: return ColorScheme.dark
case .light: return ColorScheme.light
}
}

func setAppDisplayMode() {
var userInterfaceStyle: UIUserInterfaceStyle
switch self {
case .system: userInterfaceStyle = UITraitCollection.current.userInterfaceStyle
case .dark: userInterfaceStyle = .dark
case .light: userInterfaceStyle = .light
}
let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene
scene?.keyWindow?.overrideUserInterfaceStyle = userInterfaceStyle
}
}

@AppStorage("displayMode") var displayMode = DisplayMode.system

var body: some View {
HStack {
Text("Display mode:")
Picker("Is Dark?", selection: $displayMode) {
Text("System").tag(DisplayMode.system)
Text("Dark").tag(DisplayMode.dark)
Text("Light").tag(DisplayMode.light)
}
.pickerStyle(SegmentedPickerStyle())
.onChange(of: displayMode) { newValue in
print(displayMode)
displayMode.setAppDisplayMode()
}
}
}
}

Swift constants: Struct or Enum

Both structs and enumerations work. As an example, both

struct PhysicalConstants {
static let speedOfLight = 299_792_458
// ...
}

and

enum PhysicalConstants {
static let speedOfLight = 299_792_458
// ...
}

work and define a static property PhysicalConstants.speedOfLight.

Re: A struct will be copied every time i use it or not?

Both struct and enum are value types so that would apply to enumerations as well. But that is irrelevant here
because you don't have to create a value at all:
Static properties (also called type properties) are properties of the type itself, not of an instance of that type.

Re: What advantages has the choice of a struct or enum?

As mentioned in the linked-to article:

The advantage of using a case-less enumeration is that it can't accidentally be instantiated and works as a pure namespace.

So for a structure,

let foo = PhysicalConstants()

creates a (useless) value of type PhysicalConstants, but
for a case-less enumeration it fails to compile:

let foo = PhysicalConstants()
// error: 'PhysicalConstants' cannot be constructed because it has no accessible initializers

What is the best way to introduce a custom UIColor to a Swift project?

Answer: Extension, in my professional opinion.

Think about it; you are, in philosophy, 'extending' the range of colors offered by UIColor. Provided that your color name is distinct and the new function follows Apple's method naming protocol (i.e. <color name>Color), extending UIColor seems neater. One or two new colors (in my opinion) don't warrant an entire dedicated struct.


Bonus answer:
Where would a struct (or enum!) be a good fit?

  • If your app forces replacements for the standard colors (i.e. custom
    'primary' colors)
  • If your app is specifically designed to be themed / customized, it might be good have an enum, to serve as a concrete list for available options.
  • If you can't think of standard names for the colors (sharkBlueColor, anyone?).
  • If your app is specifically for drawing/painting (in which case a 'palette' construct might be good idea).

... the list goes on. You must learn to discern and decide for yourself as you mature as a Swift developer!

Swift update UIColor when change color

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!



Related Topics



Leave a reply



Submit