Tuesday, May 18, 2010

Programmatic Gradient Buttons

Thanks to a little insomnia, I decided to play around a little more with doing a programmatic gradient button. I started to create another gradient button, realized that the code was going to be 90% the same between the two style gradient buttons, so did a little refactoring. The result is a class you can subclass to easily make new gradient buttons. All you have to do is override three methods to specify the gradient to use when the button is in its normal state, the gradient to use when the button is in highlighted (pressed) state, and the corner radius. Here's what the alert-style button looks like now:

BlueGradientButton.h
#import <UIKit/UIKit.h>
#import "AbstractGradientButton.h"

@interface BlueGradientButton : AbstractGradientButton
{
}


@end


BlueGradientButton.m
#import "BlueGradientButton.h"

@implementation BlueGradientButton
- (CGGradientRef)createNormalGradient
{
CGFloat locations[3];
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
NSMutableArray *colors = [NSMutableArray arrayWithCapacity:3];
UIColor *color = [UIColor colorWithRed:0.283 green:0.32 blue:0.414 alpha:1.0];
[colors addObject:(id)[color CGColor]];
locations[0] = 0.0;
color = [UIColor colorWithRed:0.82 green:0.834 blue:0.87 alpha:1.0];
[colors addObject:(id)[color CGColor]];
locations[1] = 1.0;
color = [UIColor colorWithRed:0.186 green:0.223 blue:0.326 alpha:1.0];
[colors addObject:(id)[color CGColor]];
locations[2] = 0.483;

CGGradientRef ret = CGGradientCreateWithColors(space, (CFArrayRef)colors, locations);
CGColorSpaceRelease(space);
return ret;
}

- (CGGradientRef)createHighlightGradient
{
CGFloat locations[4];
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
NSMutableArray *colors = [NSMutableArray arrayWithCapacity:4];
UIColor *color = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:1.0];
[colors addObject:(id)[color CGColor]];
locations[0] = 0.0;
color = [UIColor colorWithRed:0.656 green:0.683 blue:0.713 alpha:1.0];
[colors addObject:(id)[color CGColor]];
locations[1] = 1.0;
color = [UIColor colorWithRed:0.137 green:0.155 blue:0.208 alpha:1.0];
[colors addObject:(id)[color CGColor]];
locations[2] = 0.51;
color = [UIColor colorWithRed:0.237 green:0.257 blue:0.305 alpha:1.0];
[colors addObject:(id)[color CGColor]];
locations[3] = 0.654;

CGGradientRef ret = CGGradientCreateWithColors(space, (CFArrayRef)colors, locations);
CGColorSpaceRelease(space);
return ret;
}

- (CGFloat)cornerRadius
{
return 7.0;
}

@end



Adding new gradient buttons is relatively simple, since all the heavy drawing is handled in the superclass based on the values you return in these three methods. You can check out the sample project that has two gradient buttons: one that simulates the alert style button and which also works for regular bar buttons, and one that simulates the red delete button. I'll likely add more styles later.

One important notice - the CFGradientRef returned by the two methods should not be released, the super class will release it when it's done. This is a bit of an oddity, but there is no CFGradientAutorelease() function, only a CFGradientRelease() function. I changed the name to start with "create". In the Core Foundation memory management rules, functions that use create return an object with retain count of 1. There probably is a more elegant way to deal with this situation, but none occurred to me and I didn't want to have to mess around with a callback to release the memory given how straightforward the usage is here.

You can download the project here. I've also added the project to Google Code if anyone wants to contribute additional elements.



5 comments:

thealpha said...

Loved you post. I created a similar example project and your example code will help me enhance what I am using.

http://blog.corywiles.com/creating-custom-glossy-gradient-buttons-progr

easco said...

None of the Core Graphics objects have autorelease methods... there's no autorelease in the Core Foundation model.

You should be able to autorelease the gradient using something like:

[NSMakeCollectable(gradient) autorelease]

The use of NSMakeCollectable is odd... you could do something like:

[(id)gradient autorelease]

But if your code ever managed to squeak it's way into a Garbage Collected environment the NSMakeCollectable with the autorelease should do the right thing.

Jeff LaMarche said...

Actually, I think the right way to deal with this is not to be creating and releasing them every time it's drawn. Instead, I should make them instance variables, create the gradient the first time it's called, and release them in dealloc.

Yeah, that's the right approach. Hopefully I can get around to fixing it before too long. Not going to be in this evening - somebody remind me later, willya?

Viejo Joven said...

Hi jeff, was this fixed?

thanks!

h4ns said...

What youre saying is completely true. I know that everybody must say the same thing, but I just think that you put it in a way that everyone can understand. I also love the images you put in here. They fit so well with what youre trying to say. Im sure youll reach so many people with what youve got to say.

Arsenal vs Huddersfield Town live streaming
Arsenal vs Huddersfield Town live streaming
Wolverhampton Wanderers vs Stoke City Live Streaming
Wolverhampton Wanderers vs Stoke City Live Streaming
Notts County vs Manchester City Live Streaming
Notts County vs Manchester City Live Streaming
Bologna vs AS Roma Live Streaming
Bologna vs AS Roma Live Streaming
Juventus vs Udinese Live Streaming
Juventus vs Udinese Live Streaming
Napoli vs Sampdoria Live Streaming
Napoli vs Sampdoria Live Streaming
Fulham vs Tottenham Hotspur Live Streaming
Fulham vs Tottenham Hotspur Live Streaming
AS Monaco vs Marseille Live Streaming
AS Monaco vs Marseille Live Streaming
Alajuelense vs Perez Zeledon Live Streaming
Alajuelense vs Perez Zeledon Live Streaming
Technology News | News Today | Live Streaming TV Channels