Swift Swift: Using UIColor in Swift Part 2: Making a Color Palette with HSB

[Updated to Swift 2.0/iOS9 SJL 9/18/15]
In our last post we discussed the RGB color space which uses red green and blue to create color. It is a very popular color space to communicate and specify color since it uses a very easy numbering system to make up millions of colors. In Swift the key initializer for a RGB color is UIColor(red:,green:,blue:,alpha:). Beyond a few easy cases, it becomes very difficult to match colors to make a good palettes for user interfaces. Sometimes we want to make a series of related colors, such as a rainbow, and RGB makes this near impossible to do programmatically.

The HSB Color Space

Unlike RGB, HSB is based primarily on hue. Hue assigns numbers to every color in the rainbow in order of the spectrum: in terms of our RGB system colors, that spectrum is red, yellow, green, cyan, blue and magenta. After magenta, color becomes more red so that we are back to red again, making a wheel of colors:

RGB Color Wheel by Hue
RGB Color Wheel by Hue

Besides hue, there are two more settings: saturation and brightness. Saturation is a scale from a full hue to gray with no hue. Brightness is a scale from white to black in a color. At full saturation and full brightness, a color is what we will call a pure hue.  Some examples of saturation and brightness changes might look like this on our color wheel, which becomes more of a star:

Color Star with stauration change and brightness change.
Color Star with saturation change and brightness change.

The RYB Colors

Why are orangeColor and purpleColor Apple system colors? There is another way of looking at color, but it does not work easily on computer systems. According to physics, the three primary colors are red, green and blue. Color psychology, and almost any art student from pre-school to graduate school will tell you the primary colors are red, yellow and blue. You get a different set of primary and secondary colors if you use RYB instead of RGB. The system colors look like this:

RYB color set
RYB color set

The color star with variations of hue, saturation and brightness might look like this:
RYB Color Star

Much of color is subjective, so you may have different results with different people, but many people prefer the RYB colors, particularly for matching. RYB and RGB may be a learned thing. It may also have to do with color transmitted as light, as in my iPhone and Mac Air, and reflected light, like my paint. Though I can’t find research on it, I believe younger people who learned their colors on iPads and the web where coloring programs use RGB will have a different sense of color than those of us who are older, since we grew up with finger paints and crayons which are RYB based.

Even though RYB looks better to most people, RYB doesn’t exist in any form in Xcode. We stick with RGB and HSB based on RGB. If you use RYB, you have to figure out your palette first and specify the numbers manually. Apple added orangeColor and purpleColor to help with those who want a six color RYB palette, but purple is really magenta with a 50% brightness, not a pure hue.

A Two Part Color App

After all this theory, it’s time for a little coding for color.  We’re going to make two projects in one app using the Tab Bar controller template. Create a new project in Xcode using the Tabbed Application template.

2015-09-18_13-00-25

Name it SwiftHSBColorDemo.with Swift as the language. Save the project.

Go to the storyboard. This sets up two view controllers FirstView and SecondView connected by a tab bar controller.

2015-09-18_13-14-13

In the First View, drag a button  centered under the two existing labels. Control-Drag from the button to the label with the smaller text. When the text highlights, release the mouse button. and you will get a menu like this:

2015-09-18_13-03-38

Holding down the shift key, Click Vertical Spacing and Center Horizontally.

2015-09-18_13-04-23

Press return.Using Autolayout, We constrained the button to the view. Your view shold look like this:

2015-09-18_13-05-14

Go to the Second view. Delete the smaller text label Leaving View two. Click on the view two label. Change the background to white. With the label still selected, cick open the resolver menu 2015-09-18_14-20-42. Select clear constraints.

2015-09-18_14-20-25

select the Pin menu 2015-09-18_13-06-24. In the pin menu set the label to so we have 0 left, 0 right and 30 bottom, Update frame with Items of new constraints:

2015-09-18_14-21-08

Your view will now look like this:

2015-09-18_13-06-41

HSB in Swift

Go to the FirstViewController.swift file. In the code, change viewDidLoad to the following:

override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = UIColor(
        hue: 1.0,
        saturation: 1.0,
        brightness: 1.0,
        alpha: 1.0)
}

Just like the RGBA initializer we learned in part one UIColor has an initializer for HSBA, where A is alpha. For hues, both 0.0 and 1.0 are red. The rest of the colors of the RGB color wheel or color star are equally spaced apart from each other.
If you build and run this small app, you will have a red background. Change the hue to any value between 0.0 and 1.0 and you will get a different background color.

Cycling Through Colors

One disadvantage to RGB is it cannot cycle through the spectrum easily. However with HSB we can do this. Add the following code to FirstViewController:

var backgroundHue:CGFloat = 0.0
@IBOutlet weak var colorMyBackground: UIButton!
@IBAction func colorMyBackground(sender: UIButton) {
    backgroundHue = backgroundHue + (1.0/12.0)
    if backgroundHue >= 1.0 {
        backgroundHue = backgroundHue - 1 
    } //cycle through 0 to 1
    view.backgroundColor = UIColor(
        hue: backgroundHue,
        saturation: 1.0,
        brightness: 1.0,
        alpha: 1.0)

    sender.setTitleColor(
        UIColor.blackColor(),
        forState: .Normal)
}

Using the assistant editor, connect the outlet and the action to the button.

We first initialize a property in the view controller class to hold the current value of the hue. When a user presses a button, the hue changes by 1/12, making on a new hue, if the hue is greater than 1, we subtract 1 from it, bringing it back into the range 0.0 to 1.0.

Matching Colors Without Looking

In a minority of people like me, we many not see some of these colors. Color blind people don’t see in black and white but some colors look the same. If you took some RGB sliders, I can illustrate the phenomenon. In my case if I start with yellow, and slide the red slider left to decrease the red, I still see yellow, though most of you are seeing more and more green. About two thirds to the left, the color gets darker, but not more green.

Yellow to green I can't see.
Yellow to green I can’t see.

Complements

I cannot trust my eyes. Instead, I learned to trust the color wheel. If you look at those primary colors in either RGB or RYB, there is a pattern. We have three primaries and three secondaries.

Primaries and Secondaries in RGB
Primaries and Secondaries in RGB

A secondary is a mix of two of the three primaries. So here’s the rule: the color with most amount of two colors and the least amount of the third color matches the third color best. We call this a complement. If you look at the colors on a circle, you can see this. There is one more important fact: the complement is halfway around the circle.

Add 0.5 to the hue for a complement
Add 0.5 to the hue for a complement

Therefore we can add 0.5 to the hue to calculate the complement. Since we must keep the range of 0.0 to 1.0, if a value is grater then 1, subtract 1.

Change colorMyBackground to this:

@IBAction func colorMyBackground(sender: UIButton) {
    backgroundHue = backgroundHue + (1.0/12.0)
    if backgroundHue >= 1.0 {
        backgroundHue = backgroundHue - 1 
    }
    view.backgroundColor = UIColor(
        hue: backgroundHue,
        saturation: 1.0,
        brightness: 1.0,
        alpha: 1.0)

    var complement = backgroundHue + 0.5  //find the complement
    if complement >= 1.0 {
        complement = complement - 1
    }
    sender.backgroundColor = UIColor(
        hue: complement,
        saturation: 1.0,
        brightness: 1.0,
        alpha: 1.0)

    sender.setTitleColor(
        UIColor.blackColor(),
        forState: .Normal)
    }

Build and run, now the button background is the complement of the view background each time you tap the button.

Triads

Two colors are usually not that useful. Three colors work a lot better. We call three hues triads, and like complements they are the color farthest from the other colors. Triads are 1/3 around the circle.

Triads are 1/3 around the circle.
Triads are 1/3 around the circle.

To code this, we add 1/3 to the current hue to find a triad and 1/3 again to find the second triad hue. Add outlets named triadLabel1 and triadLabel2. outlets for our labels and connect them up to the labels on View One.

@IBOutlet weak var triadLabel1: UILabel!
@IBOutlet weak var triadLabel2: UILabel!

 

Add this code under the code for complements.

var triad1 = backgroundHue + (1.0/3.0)
if triad1 >= 1.0 {
    triad1 = triad1 - 1
}
triadLabel1.backgroundColor = UIColor(
    hue: triad1,
    saturation: 1.0,
    brightness: 1.0,
    alpha: 1.0)

var triad2 = backgroundHue + (2.0/3.0)
if triad2 >= 1.0 {
    triad2 = triad2 - 1
}
triadLabel2.backgroundColor = UIColor(
    hue: triad2,
    saturation: 1.0,
    brightness: 1.0,
    alpha: 1.0)

Build and run. You now can see the complements and the triads

2015-09-18_15-22-37

Even More Colors

Once you get past three hues the possibilities are endless–Sort of. There are a few important rules about color that works well:

  1. The best n hues are 1/n
  2. Use as few colors as possible
  3. Contrast colors as much as possible
  4. If you break rule 2, break rule 3, but with saturation or brightness, not hue
  5. You don’t have to be 100% accurate in your triad(the RYB rule)
  6. Use black and white

Rule #5 can use  the complement of red as a good example. Does Red look good with cyan? (RGB) or does it look good with green(RYB)?  The answer is both. You do have some wiggle room in hues around a complement to pick a color as well. There is even one triad called a split complement which takes two colors  slightly off the complement of a color, one clockwise, one counter clockwise. The three hues of a  possible split complement for red would be 1.0, 0.45,0.55

Split complement of Red
Split complement of red

Using the  color star with saturation and brightness, you can use these colors for example as triads. Here are examples with changes in saturation and brightness to make lighter and darker colors:

Triads with saturation and brightness changes
Triads with saturation and brightness changes

In one triad I used a saturated yellow,  dark cyan and dark magenta. The other I saturated green and blue and used a dark red. All of these match well.

Lace Hotness (Oct 2012) Watercolor using trichrome technique (Sepia, Paynes Gray, New Gamboge) Click painting for model  and photo reference information.

Why my paintings look so colorful is the lack of number of colors. The painting at right is only three colors, but appears to be many more. I used the white of the paper as a color as well.  Most beginning artists (and UI designers) make the mistake of too many colors. What happens then is a blend into one ugly gray. Keep your colors as few as possible. The only exception is the use of gradients. One example of gradients is the Message app. Apple slightly colors cells differently, based if they are on the top or bottom of the view. Change those by saturation or brightness very slightly, not by hue.

Rainbow Buttons and Color Pickers

Sometimes you need a color chooser or picker. To set the colors for such a thing in RGB color space is difficult, though not impossible. With what we have done with HSB so far, it should be clear that an HSB approach makes the most sense. What we will do is make a series of buttons, and set their backgroundColor property appropriately.

In the SecondViewController class, add the following method:

func makeRainbowButtons(buttonFrame:CGRect, sat:CGFloat, bright:CGFloat){
    var myButtonFrame = buttonFrame
    for i in 0..<12{  
        let hue:CGFloat = CGFloat(i) / 12.0 
        let color = UIColor(
            hue: hue, 
            saturation: sat,
            brightness: bright,
            alpha: 1.0) 
        let aButton = UIButton(frame: myButtonFrame) 
        myButtonFrame.origin.x = myButtonFrame.size.width + myButtonFrame.origin.x 
        aButton.backgroundColor = color
        view.addSubview(aButton)
    }
}

We create a loop for 12 colors. We’ll compute the color by finding the result of n/12 as a CGFLoat as we discussed earlier. We make a UIColor from our computed hue with a given saturation and brightness and an alpha of 1. We make a button and assign a color to its background, and finally add the button as a subview of view. If you want spacing between the buttons, you can add a constant which computes a new position for the next button, but I left them next to each other.

Now change viewDidLoad to this:

//MARK: - Life Cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        view.backgroundColor = UIColor.blackColor()
        let buttonFrame = CGRect(
            x: 12,
            y: 100,
            width: 25,
            height: 25)
        makeRainbowButtons(
             buttonFrame, 
             sat: 1.0, 
             bright: 1.0)
    }

build and run. Move to the second tab:

2015-09-18_18-05-57

You now have a rainbow on your screen. We can make the rainbow show the saturation of colors by adding another loop to viewDidLoad:

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib. 
   view.backgroundColor = UIColor.blackColor()
   let buttonFrame = CGRect(
       x: 12,
       y: 100,
       width: 25,
       height: 25)
   var i:CGFloat = 1.0
   while i &gt; 0{
       makeRainbowButtons(buttonFrame, sat: i ,bright: 1.0)
       i = i - 0.1
       buttonFrame.origin.y = buttonFrame.origin.y + buttonFrame.size.height
   }
}
 

Build and run:

2015-09-18_18-05-01

This app does nothing since these buttons has no target-action, or anywhere to put an action if it did. Let’s add an action to the buttons. In makeRainbowButtons under the addSubview, add the following:

aButton.addTarget(
    self, action: "displayColor:",
    forControlEvents: UIControlEvents.TouchUpInside)

For our action let’s print the values for the colors both in RGB and HSB. To do that, we’ll read the button’s background as a UIColor and use the getHue and getRed methods to pull the values from the UIColor.

func displayColor(sender:UIButton){
    var r:CGFloat = 0,
        g:CGFloat = 0,
        b:CGFloat = 0,
        a:CGFloat = 0,
        h:CGFloat = 0,
        s:CGFloat = 0,
        l:CGFloat = 0
    let color = sender.backgroundColor!
    if color.getHue(
        &h, 
        saturation: &s,
        brightness: &l,
        alpha: &a)
    {
        if color.getRed(
            &r, 
           green:&g,
          blue: &b,
          alpha: &a){
        let formatString = "HSB: %4.2f,%4.2f,%4.2f RGB:%4.2f,%4.2f,%4.2f"
        let colorText = String(
            format: formatString,
            Float(h),
            Float(s),
            Float(b),
            Float(r),
            Float(g),
            Float(b))
        colorNumberLabel.text = colorText
     }
 }
}

As we discussed in the last post, getRed() uses some downright evil types for parameters. Instead of using those types, we pass the values back using an & in the parameter. Both getHue and getRed return a Boolean value if there is a color, so we nest two if statements together to assure we have a color. Now that we have those numbers we form and print them in a label. Of course, that means we have to set up a label.

Control – drag the label to the assistant editor and make an outlet named colorNumberLabel.  Change the label’s fint to 20point and left justified to make sure it fits on the phone. Build and run. You have the colors identified in HSB and RGB.

2015-09-18_18-03-53

This  and the last color post covered a lot of ground. Whole books have been written about color and there are even graduate degrees in color theory. What we covered are mere basics. That said, these two  posts will give you a good basis to make color pickers, communicate to to others about colors, work with colors in Swift, to use and match your own colors decently.

The Whole Code

FirstViewController

//
//  FirstViewController.swift
//  SwiftHSBColor
//
//  Created by Steven Lipton on 10/5/14.
//  Copyright (c) 2014 MakeAppPie.Com. All rights reserved.
//  Updated for Swift 2.0/iOS9 9/18/15

import UIKit

class FirstViewController: UIViewController {
    
    //MARK: First Example -- Complements and triads
    
    var backgroundHue:CGFloat = 0.0
    
    @IBOutlet weak var triadLabel1: UILabel!
    
    @IBOutlet weak var triadLabel2: UILabel!
    
    @IBOutlet weak var colorMyBackground: UIButton!
    
    @IBAction func colorMyBackground(sender: UIButton) {
        backgroundHue = backgroundHue + (1.0/12.0)
        
        if backgroundHue >= 1.0 {
            backgroundHue = backgroundHue - 1
        }
        view.backgroundColor = UIColor(hue: backgroundHue,
            saturation: 1.0,
            brightness: 1.0,
            alpha: 1.0)
        
        var complement = backgroundHue + 0.5
        if complement >= 1.0 {
            complement = complement - 1
        }
        sender.backgroundColor = UIColor(
            hue: complement,
            saturation: 1.0,
            brightness: 1.0,
            alpha: 1.0)
        
        var triad1 = backgroundHue + (1.0/3.0)
        if triad1 >= 1.0 {
            triad1 = triad1 - 1
        }
        triadLabel1.backgroundColor = UIColor(
            hue: triad1,
            saturation: 1.0,
            brightness: 1.0,
            alpha: 1.0)
        
        var triad2 = backgroundHue + (2.0/3.0)
        if triad2 >= 1.0 {
            triad2 = triad2 - 1
        }
        self.triadLabel2.backgroundColor = UIColor(
            hue: triad2,
            saturation: 1.0,
            brightness: 1.0,
            alpha: 1.0)
        
        sender.setTitleColor(
            UIColor.blackColor(),
            forState: .Normal)
    }
    
    //MARK: - Life Cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        //view.backgroundColor = UIColor(hue: 1.0, saturation: 1.0, brightness: 1.0, alpha: 1.0)
        view.backgroundColor = UIColor.whiteColor()
        colorMyBackground.setTitleColor(UIColor.blackColor(), forState: .Normal)
    }
}

SecondViewController

//
//  SecondViewController.swift
//  SwiftColorPicker
//
//  Created by Steven Lipton on 10/7/14.
//  Copyright (c) 2014 Steven Lipton. All rights reserved.
//  Updated to Swift2.0/iOS9 SJL 9/18/15

import UIKit

class SecondViewController: UIViewController {
    
    //MARK: - Second example - rainbow buttons
    let formatString = "HSB: %4.2f,%4.2f,%4.2f RGB: %4.2f,%4.2f,%4.2f"
    @IBOutlet weak var colorNumberLabel: UILabel!
    
    func displayColor(sender:UIButton){
        var r:CGFloat = 0,
            g:CGFloat = 0,
            b:CGFloat = 0,
            a:CGFloat = 0,
            h:CGFloat = 0,
            s:CGFloat = 0,
            l:CGFloat = 0
        let color = sender.backgroundColor!
        if color.getHue(
            &h, saturation: &s,
            brightness: &l,
            alpha: &a)
        {
            if color.getRed(
                &r,
                green: &g,
                blue: &b,
                alpha: &a)
            {
                let colorText = String(
                    format: formatString,
                    Float(h),
                    Float(s),
                    Float(b),
                    Float(r),
                    Float(g),
                    Float(b))
                colorNumberLabel.text = colorText
            }
        }
        
    }
    





    func makeRainbowButtons(
        buttonFrame:CGRect,
        sat:CGFloat,
        bright:CGFloat)
    {
        var myButtonFrame = buttonFrame
        //populate an array of buttons
        for i in 0..<12{
            let hue:CGFloat = CGFloat(i) / 12.0
            let color = UIColor(
                hue: hue,
                saturation: sat,
                brightness: bright,
                alpha: 1.0)
            let aButton = UIButton(frame: myButtonFrame)
            myButtonFrame.origin.x = myButtonFrame.size.width + myButtonFrame.origin.x
            aButton.backgroundColor = color
            view.addSubview(aButton)
            aButton.addTarget(
                self,
                action: "displayColor:",
                forControlEvents: UIControlEvents.TouchUpInside)
        }
    }
    
    //MARK: - Life Cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib. 
        view.backgroundColor = UIColor.blackColor()
/* single row of buttons*/
        let buttonFrame = CGRect(
            x: 12,
            y: 100,
            width: 25,
            height: 25)
        makeRainbowButtons(buttonFrame, sat: 1.0, bright: 1.0)
        
/* multiple buttons
        
        var i:CGFloat = 1.0
        var buttonFrame = CGRect(
            x: 12,
            y: 100,
            width: 25,
            height: 25)
        while i > 0{
            makeRainbowButtons(buttonFrame, sat: i ,bright: 1.0)
            i = i - 0.1
            buttonFrame.origin.y = buttonFrame.origin.y + buttonFrame.size.height
        }*/

    }

}

10 Replies to “Swift Swift: Using UIColor in Swift Part 2: Making a Color Palette with HSB”

    1. Increase the loop by the number of columns you want. Set up an if statement to state if you are over the number of color columns( for the demo program 12 so make it 13) , use a different color scheme. In that color scheme use UIColor(white:alpha:) for color and for the current method use saturation for your value for white. That should get you a column of grays. For brown, there is not a good way. stick a button on the bottom of the picker with browncolor() is the best I can suggest.

      1. Thanks for your replay.How can i use saturation to whilte, is same as hue calculation .If you give codes means will help me

      2. I’m sorry, but I do not write code in the comments anymore. I’ve already answered that question in my last answer to you. Use the UIColor class method UIColor(white:alpha:) instead of UIColor(hue:saturation:brightness:alpha) for your gray scale.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s