There comes a time in many iOS developers’s careers that they will need to save a reference to a color itself. Maybe it is just to change the background color of a view, maybe it is for custom drawing, or any number of other reasons.
This is where UIColor comes in. UIColor is particularly optimized to work on iOS devices, so while there are older similar classes like CGColor or CIColor, you should probably use UIColor, unless you are working on some app that needs to translate between different colorspaces.
Built-In UIColor Presets
There are a few built in colors, which are accessed with class variables. These are the available preset colors:
Method | Sample |
black | |
darkGray | |
lightGray | |
white | |
gray | |
red | |
green | |
blue | |
cyan | |
yellow | |
magenta | |
orange | |
purple | |
brown | |
clear |
If you need to store these, like for a background color, you would use the code:
let backgroundColor = UIColor.blue
Notice that, since this is a class variable, you call these presets directly from the UIColor class, without having to make an instance of it.
Creating a custom UIColor Object
Now, the presets above are useful, but you probably will want to make your own color that is somewhere between those available colors. In Swift, you instantiate a custom UIColor object with an initializer.
There are several available to us, some more useful than others:
-
- init(white: CGFloat, alpha: CGFloat)
- init(hue: CGFloat, saturation: CGFloat, brightness: CGFloat, alpha: CGFloat)
- init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat)
- init(displayP3Red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat)
- init(cgColor: CGColor)
- init(patternImage image: UIImage)
- init(ciColor: CIColor)
- init(dynamicProvider: @escaping (UITraitCollection) -> UIColor)
Each of the ones that take a CGFloat take a value between 0.0 and 1.0, referring to either the complete absence of, or the maximum amount of that color component respectively. So, this means that even if you have pure RGB values in decimal or hexadecimal format, you will have to divide them by decimal 255 to get the amount to input here.
We will primarily be talking about the third one on this post. But, for the sake of knowledge, I’ll quickly touch on the others here.
The first one with the “white” parameter is used to easily create grayscale values. With 0% whiteness (0.0), you get black, with 100% whiteness (1.0) you get pure white. Between those values you get progressively lighter grays as you go from 0 to 1.
The Hue-Saturation-Brightness one is still RGB based, but is another way of representing it, aiming to make it more intuitive. I personally don’t know much about it, so I’ll point you to the HSL and HSV Wikipedia page (HSV (Hue-Saturation-Value) is another name for HSB).
The CGColor object is a Core Graphics based color (which is lower level, and more general than UIColor). The CIColor is a Core Image color, apparently used in Core Image based filters.
Finally there is the patternImage, which apparently tiles an image in to fill the area that is needing this “color”. You can read more about it at Keith Harrison’s blog article Using Pattern Images to Set Background Views.
So, then we just make an new color with one of these initializers and store it in a variable, like so:
let swiftColor = UIColor(red: 1, green: 165/255, blue: 0, alpha: 1)
There are a few things to note here. First of all, what is alpha? Alpha is simply a measurement of how opaque something is. These colors can be made completely transparent with an alpha value of 0, or completely opaque with an alpha of 1.
Secondly, for those that particularly care about types, you will notice something odd. CGFloat is similar to NSInteger, and even the Swift Integer type. It is a different way of storing a floating point value that depends on the system it is running on. On a 32-bit system a CGFloat is a typealias for a simple Float. On a 64-bit system, a CGFloat is a typealias for a Double. Here, you can clearly see that we have Swift Integers being used for these parameters and calculations.
I’m not entirely sure of the mechanisms here, but through testing I saw that you could use a Swift Double literal here ( so 1.0, 165.0/255.0, 0.0, 1.0) or the Integer literal form above and it accepted it. I would guess that it has something to do with the type inference system.
However, Integer and Double are not actually toll-free bridged over. If I make a separate variable and explicitly type it as a Swift Integer or Double type, it gives errors like “Cannot convert value of type ‘Int’ to expected argument type ‘CGFloat’.” If the variable is explicitly typed as a CGFloat, it will accept it. You can make that variable explicitly typed as a CGFloat with either the literal “165/255” or “0.0/255.0”, so that is why I suspect it has something to do with inferring the typing, since the requirement of a CGFloat in that method gives the type system enough information to determine that it should interpret those literals as Floating point numbers. It even tells it enough so that you don’t get the Integer division answer there, so:
let answer: CGFloat = 165/255 //The answer now stores the value "0.647058823529412".
Instead of the integer division answer of 0 (since it is less than one, it just truncates the decimal component in Integer division).
A handy extension
How often do you make colors with less than 100% opacity? If you don’t do so very often, perhaps you could make a convenience initializer that just assumes that the alpha value is 100%, and fills it in for you.
This sounds like a great place to use an extension. The built in version is definitely the most versatile, but we can make our code easier to read by simply making an extension to UIColor that fills in some things for us. In fact, it is probably more common to have RGB values than decimal percentages for each color, so let’s make this extension take Integers as parameters too. This extension would then look like:
extension UIColor { convenience init(red: Int, green: Int, blue: Int) { let newRed = CGFloat(red)/255 let newGreen = CGFloat(green)/255 let newBlue = CGFloat(blue)/255 self.init(red: newRed, green: newGreen, blue: newBlue, alpha: 1.0) } }
We now have a much cleaner way of creating a UIColor. After declaring this extension somewhere in your app’s module, you can do this:
//With the extension let newSwiftColor = UIColor(red: 255, green: 165, blue: 0)
One other cool thing to note, Swift’s Numeric Literal system can even accept literals in a few different bases, including hexadecimal itself! That means, that with no other code on our part, you can actually put the hexadecimal numbers into this initializer as well, like so:
//With fancy Swift Numeric Literals let fancySwiftColor = UIColor(red: 0xFF, green: 0xA5, blue: 0)
You just have to prefix the numbers with the “0x” (just in case it is not clear, that is the number zero, followed by a lowercase letter ‘X’).
Pretty neat way to input color values to a UIColor, eh?
Conclusion
As a little bit of history, in Objective-C also used UIColor, but you had the choice of either creating colors with the initializers (like above) or via factory methods. In the documentation for UIColor, both are shown, but only the initializers show a Swift implementation. The basic difference between using one or the other was whether your class took ownership of the the UIColor created, or not. Using an initializer, your class would take ownership, however if you used the factory method, it would not. Since ARC (Automatic Reference Counting) was added, this distinction made little difference. Beforehand though, if you used the initializer, you were responsible for decreasing the “retain count” of these colors, getting them one step closer to being dropped from memory when the system no longer needed them.
So the distinction was there, and rather important earlier on in Objective-C. Then ARC came, and made these practically the same to most programmers. Now with the introduction of Swift, they decided to go more to initializers than factory methods, and so decided to drop Swift support for them. It’s interesting to see how these APIs evolve over time.
I hope you found this article helpful. If you did, please don’t hesitate to share this post on Twitter or your social media of choice, every share helps. Of course, if you have any questions, don’t hesitate to contact me on the Contact Page, or on Twitter @CodingExplorer, and I’ll see what I can do. Thanks!