Category Archives: UI

Animating a Gradient in a UIView Smoothly

(Built with XCode  6.3 – Code on GitHub)

Let’s see how we can create a gradient that seems to move diagonally along a spectrum of colors as below:


The Basics

We want to subclass UIView and add an array of colors through which we are going to move.

There are some lower level CoreGraphics functions that we need to call and that are better remembered by heart 😉 So here is how drawing a simple gradient between two colors of the array looks like:

We can increment the index and the code should wrap around the array using the modulo operator.

What we now need is to interpolate through the colors as we increment the index. We will just add a UIButton to step through the array of colors assigned previously to the view.

In the main view controller just make sure to set the array of colors to the gradient view. The colors set below are primary and the reason will be explained below

For the actual animation, we will use a floating point number that goes from 0.0 to 1.0 and use a standard interpolation formula between two values:

P(x) = x_1 * (1 - factor) + x_2 * factor, factor in {0.0, 1.0}

The adds a kind of weighting to each color, the more we move towards 1.0 the less x1 matters to the sum and the more x2 takes precedence.

An RGBA color is a point in a 4-dimensional space.  All we need to do is break down the color in its 4 components and interpolate each one independently to get the result we want.

Now, I did not put the word “smoothly” on the title for no reason. An NSTimer will do the job by calling an update function where we can put our draw code but this will not necessarily be in sync with the hardware’s native refresh rate of the screen. Long story short, we would have two interrupts running on the main thread which although not catastrophic for our little example, we can do better!

Core Animation provides us with a class that links a callback to the native screen updates by the name of CADisplayLink. The callback passed will be called just before the screen gets redrawn so it provides with an ideal place to update our model, which in our case is the singular value factor.

In the “screenUpdated:” function we can now get the real time that has elapsed between redraws through the duration property of the CADisplayLink.

Then on the drawing code we interpolate as explained above.

 Wait, but why?

Yes, it’s a stolen line, so what? Now, let’s see what happened there.

Remember how we chose primary colors directly from the UIColor class? Each corner of the screen is moving circularly around the RGB color wheel returning to its initial point after 5 stops, while the screen is rendering the gradient between the two dots in the figure below:

(Note: The image above is not 100% representative of our case, as its center is white due to the fact that the colors seem to be added without weighting. In our case the result is more gray than white).

What happens if we remove a few stops from the journey?

If we run it (like in branch chroma-problem on the repo) and although we are still using primary colors, and we are still going around the same cycle, because of our fewer stops we will see a less than pleasant gray hue appearing on the screen between transitions. The problem is illustrated below:

What is happening is that the gradient will take values with little to no chroma!

Another way to think about it is, that although each color will interpolate well with the next one, their intermediaries might not. In the image below we can see that although green and blue will mix to create turquoise, and blue and red to create violet, turquoise and violet do not re-create blue!

The reason is that at this point we are mixing all 3 RGB values which will cancel each other out!

This might seem like a rigged example but I assure you that most cases of randomly chosen colors will amount to something like this. The example at the start of the article was carefully chosen to avoid this situation.

So always remember to add intermediary colors when needed, to create a compelling animation.