tumbljack

GPU Accelerated Awesomeness with CAGradientLayer

Interface

CAGradientLayer is my favorite new addition to the Core Animation framework. It’s a straightforward, hardware accelerated linear-gradient rendering API that’s easy to use, super powerful and blazing fast. Setting one up is just a few lines of code..


CAGradientLayer *layer = [CAGradientLayer layer];

layer.colors = [NSArray arrayWithObjects:(id)[UIColor colorWithWhite:0.90 alpha:1.0].CGColor, [UIColor colorWithWhite:0.75 alpha:1.0].CGColor, nil];


This renders a straight gradient, top to bottom, with the first color on top. The angle of the gradient can be adjusted by specifying two points to define an axis. CAGradientLayer carries down all the standard properties of CALayer so you get a border and corner radius for free, as well as background color. This is really convenient, because similar to working in Photoshop you can create a rendered effect using transparent whites, then adjust only the background color to change the composition you’re working with.

Tricking one out takes a little more setup, but is just a matter of adding more colors and creating a matching array of color-stop locations..


CAGradientLayer *layer = [CAGradientLayer layer];


UIColor *colorOne     = [UIColor colorWithHue:0.625 saturation:0.1 brightness:0.8 alpha:0.7];

UIColor *colorTwo     = [UIColor colorWithHue:0.625 saturation:0.1 brightness:0.8 alpha:0.7];

UIColor *colorThree  = [UIColor colorWithHue:0.625 saturation:0.1 brightness:0.8 alpha:0.5];

UIColor *colorFour    = [UIColor colorWithHue:0.625 saturation:0.1 brightness:0.8 alpha:0.2];

UIColor *colorFive     = [UIColor colorWithHue:0.625 saturation:0.1 brightness:0.8 alpha:0.0];

UIColor *colorSix       = [UIColor colorWithHue:0.625 saturation:0.1 brightness:0.8 alpha:0.0];

UIColor *colorSeven  = [UIColor colorWithHue:0.625 saturation:0.1 brightness:0.8 alpha:0.3];

NSArray *colors =  [NSArray arrayWithObjects:(id)colorOne.CGColor, colorTwo.CGColor, colorThree.CGColor, colorFour.CGColor, colorFive.CGColor, colorSix.CGColor, colorSeven.CGColor, nil];

layer.colors = colors;


NSNumber *stopOne     = [NSNumber numberWithFloat:0.00];

NSNumber *stopTwo     = [NSNumber numberWithFloat:0.02];

NSNumber *stopThree  = [NSNumber numberWithFloat:0.02];

NSNumber *stopFour    = [NSNumber numberWithFloat:0.50];

NSNumber *stopFive     = [NSNumber numberWithFloat:0.50];

NSNumber *stopSix       = [NSNumber numberWithFloat:0.95];

NSNumber *stopSeven  = [NSNumber numberWithFloat:1.00];

NSArray *locations = [NSArray arrayWithObjects:stopOne, stopTwo, stopThree, stopFour, stopFive, stopSix, stopSeven, nil];

layer.locations = locations;


The result is a scaleable, reusable, freely editable piece of hardware accelerated artwork. Entire interfaces can be created with gradient layers, using very little code, all rendered on the GPU. If fact ColorDev is drawn all in code using gradient layers, including the spectrum hue slider, the gloss effects and even the top bar. Of course you get one of those for free in the kit, but what can I say, I was geeking out.

The trick is to subclass CAGradientLayer, and set up the gradient inside of -init, then whenever you need one just instantiate it with CALayer’s factory method +layer, set its geometry and it’s ready to go.


Animation

CAGradientLayer only has a few properties: an array of colors, an array of color-stop locations, and two points to adjust the axis on which it’s rendered, but everything is animatable. You can set up a gradient animation using the key path of the property you want to animate. So animating from one color-set to another would look something like this..


CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@”colors”];

animation.fromValue = initialColors;

animation.toValue = newColors;

animation.duration = 1.0;

animation.removedOnCompletion = NO;

animation.fillMode = kCAFillModeForwards;

animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];

[gradientLayer addAnimation:animation forKey:@”animateGradient”];


Gradient animations are interesting, to say the least. It’s a little tricky getting a result that makes visual sense, but on the nerdy side, it seems like the rendering engine maps out the two gradients, then interpolates a cross-fade for each color on an per-pixel basis. It’s pretty cool. I set up a few quick examples to demonstrate the concept.


Application

A few weeks ago Matt Gallapher wrote a great post on using CAGradientLayer to implement shadow effects on a table view, as well as rendering a gradient in each table view cell. He does a neat trick by implementing UIView’s +layerClass method. This method is used to specify a custom class as the default backing layer of a UIView. Often it’s used to return a CAEAGLLayer as the views built-in layer so you can jump right into OpenGL. Matt overrides it in his UITableViewCell subclass to return a CAGradientLayer as the built-in backing layer of each table view cell. Slick.

Inspired by this I put together my own quick example implementing +layerClass all over the place to return customized versions of standard kit objects like the navigation bar, toolbar, table view cell, etc. The beauty of this is that you don’t implement -drawRect, or even touch a CGContextRef, and of course there are no images.

Not that I’m dissing Core Graphics. It’s just that it’s like this..

Core Graphics == WALL-E

CAGradientLayer == EVE

You can check out the slightly bizarre gradient animations here.

And I’m releasing my interface sample freely under the WTFPL license. So check out the code on GitHub. And there’s a demo video too.



  1. rakeshta reblogged this from tumbljack
  2. tumbljack posted this
To Tumblr, Love Metalab