Combining Colors

Ever wanted that color but a bit darker? Or to combine one color with another?

That’s what we’ll learn to do now :)

What is color to a computer?

The color of a pixel is described by the amount of red, green and blue light it emits.

In case you didn’t already know there are thousands of tiny colored lights/LEDs on the screen you are using to read this article. Under a microscope your screen looks like this:

The computer only knows how much electricity to give to each one of the 3 LEDs that represent a pixel. So if a pixel is red the green and blue lights get low or no electricity and the red one gets a lot.

RGB Color Model

The RGB color model (stands for Red Green Blue) is an additive color model in which red, green and blue light are added together in various ways to reproduce a broad spectrum of colors.

You might remember from school that red, blue and yellow are primary colors. And that is still true!

But there are two different things:

1) how colors combine if they reflect light
2) how colors combine if they emit light

When you use paint light either from the sun or from another source hits the paint and then reflcts in to your eyes. When you use pixels on a screen light is emitted from the screen and then hits your retina :)

To encode a color in RGB you need three numebrs between 0 and 255 or between 0.0 and 1.0. If you use the floating point representation it will still get converted to the discrete representation from 0 to 255 where 0 means the light is not turned on for that color and 255 that it’s turned on at the maximum level.

One thing to keep in mind is that any display only shows colors in RGB.

RGBA Color Space

RGBA(stands for red greeb blue alpha) extends the RGB color model with an extra layer of information for opacity. This is the color we actually use in our software. But if the screen does not have a alpha LED, what does it do with that information?

The alpha channel describes how much light can pass trough. So if the opacity is set to 100% the color will get shown on the screen exactly as the RGB part of the model describes it. If alpha is 0% then the pixel is fully transparent.

adding colors in rgba

3 circles – each one has a primary color and 50% opacity.

The inventors named alpha after the Greek letter in the classic linear interpolation formula (1 - α) * A + α * B.

Let’s turn some lights on!

In iOS colors are represented by the UIColor class. To create a new color you have to pass the values of the color components encoded as the floating points:

let color = UIColor(red: 1, green: 0.5, blue: 1, alpha: 1)

That will give this pinkish color:

Making all values 1 will give white. And if you make the rgb part 0 and alpha 1 you get black.

How to combine colors?

A simple way to combine colors is by using the linear interpolation formula mentioned before:

func lerp(from a: CGFloat, to b: CGFloat, alpha: CGFloat) -> CGFloat {
    return (1 - alpha) * a + alpha * b
}

lerp stands for linear interpolation and is the usual name this function is given. If you ever seen some code where there was a lot of lerping now you know what that means :)

If you want to learn more about linear interpolation you can try the lessons from Khan Academy or if you are lazy like me you can watch this amazing talk by Steven Wittens – he has more cool videos on his website:

Let’s lerp some colors:

The first thing we need to do is get the color components back from a UIColor unfortunately there are no red , green, blue, alpha properties:

extension UIColor {
    func components() -> (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
        var r: CGFloat = 0
        var g: CGFloat = 0
        var b: CGFloat = 0
        var a: CGFloat = 0

        getRed(&r, green: &g, blue: &b, alpha: &a)

        return (r, g, b, a)
    }
}

Now we can lerp colors:

extension UIColor {
    ...

    func combine(with color: UIColor, amount: CGFloat) -> UIColor {
        let fromComponents = components()

        let toComponents = color.components()

        let redAmount = lerp(from: fromComponents.red,
                               to: toComponents.red,
                            alpha: amount)
        let greenAmount = lerp(from: fromComponents.green,
                                 to: toComponents.green,
                              alpha: amount)
        let blueAmount = lerp(from: fromComponents.blue,
                                to: toComponents.blue,
                             alpha: amount)


        let color = UIColor(red: redAmount,
                            green: greenAmount,
                            blue: blueAmount,
                            alpha: 1)

        return color
    }
}

To make a lighter shade of that pink we made before we can call:

color.combine(with: .white, amount: 0.2)
BeforeAfter

OK, now let’s have some fun with what we’ve learned!

Let’s make a color picker that also show lighter and darker shades so we can browse the color space more efficiently:

picker

Step 1: Create a new Playground

Open Xcode and create a new Playground, set the platform to iOS.

Step 2: Create a View Controller

Create a subclass of UIViewController and initialize it’s view with:

class ViewController: UIViewController {
    override func loadView() {
        let width: CGFloat = 300
        let height: CGFloat = 500

        let screenSize = CGSize(width: width, 
                               height: height)
        let frame = 
            CGRect(origin: .zero,
                     size: screenSize)

        view = UIView(frame: frame)
        view.backgroundColor = .white
    }
}

Step 3: Show the view in the Playground

The first thing we need to do is to import the PlaygroundSupport module:

import PlaygroundSupport

class ColorPicker: UIViewController {
    ...
}

Create a instance of ColorPicker

let colorPicker = ColorPicker()

Set it as the liveView of the current Playground page:

PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = colorPicker.view

Open the Assistand Editor by pressing Command + Option + Return ( + + ). You should be able to see a white rectangle in the right side of the playground window.

Step 4: Basic UI

We need one sliders for each color component:

class ColorPicker: UIViewController {
    var redSlider: UISlider!
    var greenSlider: UISlider!
    var blueSlider: UISlider!

    func loadView() {
        ...

        let sliderSize = 
            CGSize(width: width - 2 * padding,                 
                  height: 30)

        let redSliderFrame =
            CGRect(origin: CGPoint(x: padding, y: 300),
                   size: sliderSize)

        redSlider = UISlider(frame: redSliderFrame)
        redSlider.addTarget(self,
                        action: #selector(didMoveSlider(_:)),
                           for: .valueChanged)

        view.addSubview(redSlider)

        let greenSliderFrame =
            CGRect(origin: CGPoint(x: padding, y: 340),
                   size: sliderSize)

        greenSlider = UISlider(frame: greenSliderFrame)
        greenSlider.addTarget(self,
                             action: #selector(didMoveSlider(_:)),
                             for: .valueChanged)

        view.addSubview(greenSlider)


        let blueSliderFrame =
            CGRect(origin: CGPoint(x: padding, y: 380),
                   size: sliderSize)

        blueSlider = UISlider(frame: blueSliderFrame)
        blueSlider.addTarget(self,
                             action: #selector(didMoveSlider(_:)),
                             for: .valueChanged)

        view.addSubview(blueSlider)
    }

    func didMoveSlider(_ slider: UISlider) {

    }
}

Note that all sliders call the didMoveSlider(_:) method when their value changes. That’s because we have to do the same thing if any of them changes – update the color on the screen.

And if we mentioned that let’s make a view to display our color:

class ColorPicker: UIViewController {
    ...

    var colorView: UIView!

    func loadView() {
        ...

        let colorViewFrame = CGRect(x: padding, y: padding, width: width - 2 * padding, height: width - 2 * padding)
        colorView = UIView(frame: colorViewFrame)
        colorView.backgroundColor = .white
        colorView.addBorder()
    }
}

The last line ads a border around the view so we can see the view even if it’s white.

extension UIView {
    func addBorder() {
        layer.borderWidth = 0.5
    }
}

At this point the UI should look like this:

Step 5: Update the color

class ColorPicker: UIViewController {
    ...

    func updateColor() {
        let redAmount = CGFloat(redSlider.value)
        let greenAmount = CGFloat(greenSlider.value)
        let blueAmount = CGFloat(blueSlider.value)

        let color = UIColor(red: redAmount,
                            green: greenAmount,
                            blue: blueAmount,
                            alpha: 1)

        colorView.backgroundColor = color        
    }

    func didMoveSlider(_ slider: UISlider) {
        updateColor()
    }
}

colorView should update when you move a slider:

Step 6: Show shades

class ColorPicker: UIViewController {
    ...

    let shadesCount = 10

    var lightShades: [UIView] = []
    var darkShades: [UIView] = []

    override func loadView() {
        ...

        let totalWidth = width - 2 * padding
        let shadeSize = totalWidth / CGFloat(shadesCount)

        for i in 0..<shadesCount {
            // light shade
            let lightShadeFrame = CGRect(x: padding + CGFloat(i) * shadeSize,
                                        y: 430,
                                        width: shadeSize,
                                        height: shadeSize)
            let lightShade = UIView(frame: lightShadeFrame)
            lightShade.backgroundColor = .white
            lightShade.addBorder()

            lightShades.append(lightShade)
            view.addSubview(lightShade)

            // dark shade
            let darkShadeFrame = CGRect(x: padding + CGFloat(i) * shadeSize,
                                        y: 430 + shadeSize,
                                        width: shadeSize,
                                        height: shadeSize)
            let darkShade = UIView(frame: darkShadeFrame)
            darkShade.backgroundColor = .white
            darkShade.addBorder()

            darkShades.append(darkShade)
            view.addSubview(darkShade)
        }
    }
}

Update shades when the color changes:

class ColorPicker: UIViewController {
    ...

    func updateColor() {
        let redAmount = CGFloat(redSlider.value)
        let greenAmount = CGFloat(greenSlider.value)
        let blueAmount = CGFloat(blueSlider.value)

        let color = UIColor(red: redAmount,
                            green: greenAmount,
                            blue: blueAmount,
                            alpha: 1)

        colorView.backgroundColor = color

        for i in 0..<shadesCount {
            let t = CGFloat(i) / CGFloat(shadesCount)

            let lightShade = lightShades[i]
            lightShade.backgroundColor =
                color.combine(with: .white, amount: t)

            let darkShade = darkShades[i]
            darkShade.backgroundColor =
                color.combine(with: .black, amount: t)
        }
    }
}

Step 7: Initalize Sliders

Set the value of each slider to 0.5 so we can test if the shades are displayed correctly:

class ColorPicker: UIViewController {
    ...

    override func viewDidLoad() {
        super.viewDidLoad()

        redSlider.value = 0.5
        greenSlider.value = 0.5
        blueSlider.value = 0.5

        updateColor()
    }
}

The screen should look like this:

Step 8: A finishing touch

Set the background color of the ColorPicker to a really white version of the selected color:

class ColorPicker: UIViewController {
    ...

    func updateColor() {
        ...

        view.backgroundColor = 
            color.combine(with: .white, amount: 0.9)
    }
}

picker

 

Download the complete Playground

Exercises

  1. Add two buttons. When you tap on one it should copy the current color. Add another view that shows color you get by adding equal amounts of the first and second button color
  2. Instead of just one view for 0.5 amount show a list similar to how we show shades.
  3. Show all colors on hex grid

hex grid

Conclusion

It’s easy and fun to create and combine colors and with a bit of common sence you can make pretty stuff :)

Hope you enjoyed this article! Please share and subscribe for more!

1 Star2 Stars3 Stars4 Stars5 Stars (9 votes, average: 5.00 out of 5)
Loading...

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Subscribe
We send about one email per week with our latest tutorials and updates
Never display this again :)