[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:

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:

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:

The color star with variations of hue, saturation and brightness might look like this:
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.
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.
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:
Holding down the shift key, Click Vertical Spacing and Center Horizontally.
Press return.Using Autolayout, We constrained the button to the view. Your view shold look like this:
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 . Select clear constraints.
select the Pin menu . 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:
Your view will now look like this:
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.

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.

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.

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.

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
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:
- The best n hues are 1/n
- Use as few colors as possible
- Contrast colors as much as possible
- If you break rule 2, break rule 3, but with saturation or brightness, not hue
- You don’t have to be 100% accurate in your triad(the RYB rule)
- 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

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:

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.

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:
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 > 0{ makeRainbowButtons(buttonFrame, sat: i ,bright: 1.0) i = i - 0.1 buttonFrame.origin.y = buttonFrame.origin.y + buttonFrame.size.height } }
Build and run:
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.
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 }*/ } }
Leave a Reply