Convert color from RGB to HSB in Swift

HSB (Hue, Saturation, and Brightness) represents color in a more human-understandable way, which closely mimics how colors are perceived in real life. Defining color using HSB instead of RGB (Red, Green, and Blue) in App design can result in a more intuitive design with the creation of harmonious color schemes and color gradients. This article shows how to convert color to HSB and RGV values using UIColor.

It is worth noting that HSB is less precise than RGB when it comes to representing the full range of colors available on modern displays. Converting colors from RGB to HSB may result in some loss of precision, however, the benefits of working with HSB in terms of usability and understandability outweigh the drawbacks.


Related articles on HSB:



Color Description

At first glance, the SwiftUI Color description property give the hex color values in the form of #RRGGBBAA representing the Red, Green, Blue and Alpha components of the color. These could be used to identify the RGB values of a color and apply a formula to convert to a corresponding HSB values for the color. The problem with this approach is that description property varies depending on how the color was defined in SwiftUI.

The description of a SwiftUI Color defined with RGB values show the RGBA hex values, whereas one of the named colors in SwiftUI returns the name. The description of a color selected from ColorPicker shows the namespace and the RGB values in decimal format.


DescriptionView

 1struct DescriptionView: View {
 2    @State var selectedColor: Color = .blue
 3    var body: some View {
 4        let color1 = Color(red: 155/255, green: 100/255, blue: 200/255)
 5        let color2 = Color.purple
 6        let color3 = Color(hue: 0.76, saturation: 0.70, brightness: 0.8)
 7
 8        VStack(alignment: .leading) {
 9            Text("Color Description")
10                .font(.largeTitle)
11                .fontWeight(.heavy)
12            Spacer().frame(height: 50)
13            
14            ColorView(title: "RGB: (155, 100, 200)", color: color1)
15            ColorView(title: "Color.purple", color: color2)
16            ColorView(title: "HSB: (0.76, 0.70, 0.80)", color: color3)
17            ColorPicker("Select a Color", selection: $selectedColor)
18                .frame(width: 250, height: 50)
19            ColorView(title: "Selected Color", color: selectedColor)
20
21            Spacer()
22        }
23        .padding(50)
24    }
25}

ColorView

 1struct ColorView: View {
 2    var title: String
 3    var color: Color
 4    
 5    var body: some View {
 6        Text(title)
 7            .font(.title)
 8        HStack {
 9            RoundedRectangle(cornerRadius: 10)
10                .fill(color)
11                .frame(width: 200, height: 50)
12            VStack(alignment: .leading) {
13                Text("description")
14                Text("\(color.description)")
15                    .fontWeight(.bold)
16            }
17        }
18        Divider()
19    }
20}

SwiftUI Color description does not always show hex values for RGB



UIColor - getHue to get Hue, Saturation & Brightness

Conversion of color from any SwiftUI color to HSB values is more easily achieved with the use of UIColor from UIKit. The color is first converted to a UIColor and the getHue(_:saturation:brightness:alpha:) method is used to set in/out properties for the hue, saturation, brightness & alpha components of the color.


ColorHueView

 1struct ColorHueView: View {
 2    var title: String
 3    var color: Color
 4    
 5    func getHsb(_ col: Color) -> (CGFloat, CGFloat, CGFloat, CGFloat) {
 6        var hue: CGFloat  = 0.0
 7        var saturation: CGFloat = 0.0
 8        var brightness: CGFloat = 0.0
 9        var alpha: CGFloat = 0.0
10        
11        let uiColor = UIColor(col)
12        uiColor.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
13        
14        return (hue, saturation, brightness, alpha)
15    }
16    
17    var body: some View {
18        let hsb = getHsb(color)
19        Text(title)
20            .font(.title)
21        HStack {
22            RoundedRectangle(cornerRadius: 10)
23                .fill(color)
24                .frame(width: 200, height: 50)
25            VStack(alignment: .trailing) {
26                Text("Hue: \(hsb.0, specifier: "%.3F")")
27                Text("Saturation: \(hsb.1, specifier: "%.3F")")
28                Text("Brightness: \(hsb.2, specifier: "%.3F")")
29            }
30        }
31        Divider()
32    }
33}

GetHueView

 1struct GetHueView: View {
 2    var body: some View {
 3        let color1 = Color(red: 155/255, green: 100/255, blue: 200/255)
 4        let color2 = Color.purple
 5        let color3 = Color(hue: 0.76, saturation: 0.70, brightness: 0.8)
 6        
 7        VStack(alignment: .leading) {
 8            Text("UIColor - getHue")
 9                .font(.largeTitle)
10                .fontWeight(.heavy)
11            Spacer().frame(height: 50)
12            
13            ColorHueView(title: "RGB: (155, 100, 200)", color: color1)
14            ColorHueView(title: "Color.purple", color: color2)
15            ColorHueView(title: "HSB: (0.76, 0.70, 0.80)", color: color3)
16            
17            Spacer()
18        }
19        .padding(40)
20    }
21}

Use UIColor.getHue to get the Hue, Saturation and Brightness components of any color



UIColor - getRed to get Red, Green & Blue

There is also a method getRed(_:green:blue:alpha:) to get the red, green, blue & alpha components from a UIColor.


ColorRedView

 1struct ColorRedView: View {
 2    var title: String
 3    var color: Color
 4    
 5    func getRgb(_ col: Color) -> (CGFloat, CGFloat, CGFloat, CGFloat) {
 6        var red: CGFloat  = 0.0
 7        var green: CGFloat = 0.0
 8        var blue: CGFloat = 0.0
 9        var alpha: CGFloat = 0.0
10
11        let uiColor = UIColor(col)
12        uiColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
13        
14        return (red, green, blue, alpha)
15    }
16    
17    var body: some View {
18        let rgb = getRgb(color)
19        Text(title)
20            .font(.title)
21        HStack {
22            RoundedRectangle(cornerRadius: 10)
23                .fill(color)
24                .frame(width: 200, height: 50)
25            VStack(alignment: .trailing) {
26                Text("Red: \(rgb.0, specifier: "%.3F")")
27                Text("Green: \(rgb.1, specifier: "%.3F")")
28                Text("Blue: \(rgb.2, specifier: "%.3F")")
29            }
30        }
31        Divider()
32    }
33}

GetRedView

 1struct GetRedView: View {
 2    var body: some View {
 3        let color1 = Color(red: 155/255, green: 100/255, blue: 200/255)
 4        let color2 = Color.purple
 5        let color3 = Color(hue: 0.76, saturation: 0.70, brightness: 0.8)
 6
 7        VStack(alignment: .leading) {
 8            Text("UIColor - getRed")
 9                .font(.largeTitle)
10                .fontWeight(.heavy)
11            Spacer().frame(height: 50)
12
13            ColorRedView(title: "RGB: (155, 100, 200)", color: color1)
14            ColorRedView(title: "Color.purple", color: color2)
15            ColorRedView(title: "HSB: (0.76, 0.70, 0.80)", color: color3)
16            
17            Spacer()
18        }
19        .padding(40)
20    }
21}

Use UIColor.getRed to get the Red, Green and Blue components of any color



Color Extension

The methods to get the HSB components from a color could be added as extensions to the Color struct if they were to be used frequently in an App.


Color+Hsb+Rgb

 1extension Color {
 2    func getHsb() -> (CGFloat, CGFloat, CGFloat, CGFloat) {
 3        var hue: CGFloat  = 0.0
 4        var saturation: CGFloat = 0.0
 5        var brightness: CGFloat = 0.0
 6        var alpha: CGFloat = 0.0
 7        
 8        let uiColor = UIColor(self)
 9        uiColor.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
10        
11        return (hue, saturation, brightness, alpha)
12    }
13    
14    func getRgb() -> (CGFloat, CGFloat, CGFloat, CGFloat) {
15        var red: CGFloat  = 0.0
16        var green: CGFloat = 0.0
17        var blue: CGFloat = 0.0
18        var alpha: CGFloat = 0.0
19
20        let uiColor = UIColor(self)
21        uiColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
22        
23        return (red, green, blue, alpha)
24    }
25}

ColorHueView2

 1struct ColorHueView2: View {
 2    var title: String
 3    var color: Color
 4    
 5    var colorHsb: (CGFloat, CGFloat, CGFloat, CGFloat) {
 6        color.getHsb()
 7    }
 8    var body: some View {
 9        Text(title)
10            .font(.title)
11        HStack {
12            RoundedRectangle(cornerRadius: 10)
13                .fill(color)
14                .frame(width: 200, height: 50)
15            VStack(alignment: .trailing) {
16                Text("Hue: \(colorHsb.0, specifier: "%.3F")")
17                Text("Saturation: \(colorHsb.1, specifier: "%.3F")")
18                Text("Brightness: \(colorHsb.2, specifier: "%.3F")")
19            }
20        }
21        Divider()
22    }
23}

HsbExtensionView

 1struct HsbExtensionView: View {
 2    @State var selectedColor: Color = .blue
 3    var body: some View {
 4        VStack(alignment: .center) {
 5            Text("Selecte a Color").font(.title)
 6            ColorPicker("Select a Color", selection: $selectedColor)
 7                .frame(width: 200, height: 50)
 8            Divider()
 9            
10            Text("Color + getHsb()")
11                .font(.largeTitle)
12                .fontWeight(.heavy)
13            Spacer().frame(height: 50)
14            
15            ColorHueView2(title: "\(selectedColor.description)",
16                          color: selectedColor)
17            Spacer()
18        }
19        .padding(40)
20    }
21}

Use getHsb extension on Color to get the Hue, Saturation and Brightness components of any color



Color Saturation and Brightness

One reason to convert a color to HSB values is that it can be more intuitive to get similar colors based on the original Hue or keep the same Hue and alter the Saturation and/or brightness. This view allows the selection of any color and the effect of reducing the Saturation and Brightness relative to the selected color.


SaturationView

 1struct SaturationView: View {
 2    var hue: CGFloat
 3    var saturation: CGFloat
 4    var brightness: CGFloat
 5    var alpha: CGFloat = 0.0
 6    
 7    var body: some View {
 8        HStack(spacing: 5) {
 9            ForEach([1.0, 0.8, 0.6, 0.4, 0.2, 0.1], id: \.self) { factor in
10                let adjustedSaturation = saturation * factor
11                VStack {
12                    RoundedRectangle(cornerRadius: 2)
13                        .fill(Color(hue: hue,
14                                    saturation: adjustedSaturation,
15                                    brightness: brightness,
16                                    opacity: alpha))
17                        .frame(width: 50, height: 80)
18                    
19                    Text("\(adjustedSaturation, specifier: "%.2F")")
20                        .font(.footnote)
21                }
22            }
23        }
24    }
25}

BrightnessView

 1struct BrightnessView: View {
 2    var hue: CGFloat
 3    var saturation: CGFloat
 4    var brightness: CGFloat
 5    var alpha: CGFloat = 0.0
 6    
 7    var body: some View {
 8        HStack(spacing: 5) {
 9            ForEach([1.0, 0.8, 0.6, 0.4, 0.2, 0.1], id: \.self) { factor in
10                let adjustedBrightness = brightness * factor
11                VStack {
12                    RoundedRectangle(cornerRadius: 2)
13                        .fill(Color(hue: hue,
14                                    saturation: saturation,
15                                    brightness: adjustedBrightness,
16                                    opacity: alpha))
17                        .frame(width: 50, height: 80)
18                    
19                    Text("\(adjustedBrightness, specifier: "%.2F")")
20                        .font(.footnote)
21                }
22            }
23        }
24    }
25}

SaturationAndBrightnessView

 1struct SaturationAndBrightnessView: View {
 2    @State var selectedColor: Color = .blue
 3    
 4    var body: some View {
 5        VStack {
 6            Text("Selecte a Color").font(.title)
 7            ColorPicker("", selection: $selectedColor)
 8                .frame(width: 20, height: 50)
 9            
10            let hsb = selectedColor.getHsb()
11            ColorHueView2(title: "Selected HSB", color: selectedColor)
12            Text("Adjusting Saturation").font(.title)
13            SaturationView(hue: hsb.0, saturation: hsb.1, brightness: hsb.2, alpha: hsb.3)
14            Divider()
15            Text("Adjusting Brightness").font(.title)
16            BrightnessView(hue: hsb.0, saturation: hsb.1, brightness: hsb.2, alpha: hsb.3)
17            Spacer()
18        }
19        .padding(40)
20    }
21}

Adjust the Saturation and Brightness of any color




Conclusion

Working with the Hue, Saturation and Brightness of a color can be more intuitive that working with Red, Green & Blue values. Reducing the saturation of color can be used to improve readability such as using a desaturated shade of blue as a background color for a text box can improve the contrast with white or black text.

Modifying Saturation and Brightness can be used to create a minimalist design, with a consistent color scheme throughout the app, resulting in a more cohesive and polished design. Reduced brightness can be used to create a sense of depth or dimension in the UI, such as using a darker shade of a color for the background of a card or button to give the appearance of elevation or depth. Brightness can also be used to indicate that a UI element is active or selected, such as a button might darken when pressed, signifying the active state.

This article showed how to convert any color in SwiftUI to its Hue, Saturation & Brightness component values with the use of UIColor from UIKit.

The source code for ColorConverterApp is available on GitHub.