DDProgressView, written by , is an easy to implement control for showing an animated progress indicator. There are three customizable values in this control, the inner color, outer color and the color of the progress indicator when empty (essentially the background color). To move the indicator forward or backward, you update a value (a float) in the control, which affects how the control is drawn.
In the screenshot above, the top progess view has an outer color of cyan and an inner color that is blue. The second bar has an outer color that is defined as [UIColor clearColor]], an inner color that is light gray and empty color that is white.
The example included with DDProgressView – which is the basis of the screenshots above – demonstrates how to create two controls, set their color values and update the progress indicator using an NSTimer with a scheduled timer interval.
Upon first glance, the UIButton class doesn’t seem to provide what you might expect in terms of customization. This often causes developers to resort to creating buttons in an image editor and then specifying that in the Background field in Interface Builder. This is a fine solution and will likely give you what you need, but with Core Animation layers there is a simpler way to achieve the look you want without having to create an image. This post will demonstrate how.
Setting a Background Color
In Interface Builder, you can specify a background color for your button if you are using the ‘Custom’ button setting, but when you run the application, the button will display as a block with no rounded corners. This is because custom buttons don’t really have any default attributes defined. It’s completely up to you to define them. Core Animation layers can help.
Note: Before I get into the source code, I want to remind you that you’ll need to add the QuartzCore framework to your project and #import <QuartzCore/QuartzCore.h> into one of your header files to have Core Animation layer support. I normally add this import statement to my precompiled header (.pch) file.
What Interface Builder doesn’t give you, you can still take advantage of by writing a little bit of code. For example, if you want your custom button to have a red background color with rounded corners and a border, you need to define an outlet in your view controller where the button will be referenced and set the following attributes on the UIButton’s layer.
With this code the layer gets a corner radius of 8.0 and the -setMasksToBounds: tells the button’s layer to mask any layer content that comes below it in the layer tree. This is necessary in order for the layer to mask off the rounded corners.
Finally, set the border width to 1.0 to display an outline around the button. The default color for this border is black. You can change it to anything you like using -setBorderColor: on the layer passing it a CGColorRef (e.g. [[UIColor greenColor] CGColor] would give you a green border).
iPhone Development Protip: Rounding corners is possible on any UIView based view. All UIViews have a root layer. You simply call -setCornerRadius: and -setMasksToBounds: on the view’s layer and your corners will be rounded. It is that simple.
You can set the background color in Interface Builder or in code–whichever you prefer. There are two ways to do this in code. One using the layer and one using the UIView call to -setBackgroundColor:.
// Core Animation way
[[button1 layer] setBackgroundColor:[[UIColor redColor] CGColor]];
// UIView way
[button1 setBackgroundColor:[UIColor redColor]];
The main difference between the two is that the layer works with a CGColorRef while the UIView works with a UIColor. It’s a subtle difference, but one that you should know.
Gradient Buttons Rule
The demo app uses some ultra-bright gaudy looking color gradients just for effect. I suggest you don’t use something so loud and obnoxious. A more subtle difference between colors will look better. Of course this is subjective, so do what you like.
To achieve this gradient look, I use a and add it to the root of the button’s layer tree. In fact, for the demo application, I created a UIButton derived class that encapsulates the gradient layer creation and drawing. Here is its implementation:
#import "ColorfulButton.h"
@implementationColorfulButton
@synthesize _highColor;
@synthesize _lowColor;
@synthesize gradientLayer;
-(void)awakeFromNib;
{
// Initialize the gradient layer
gradientLayer =[[CAGradientLayer alloc] init];
// Set its bounds to be the same of its parent
[gradientLayer setBounds:[self bounds]];
// Center the layer inside the parent layer
[gradientLayer setPosition:
CGPointMake([self bounds].size.width/2,
[self bounds].size.height/2)];
// Insert the layer at position zero to make sure the
#import "ColorfulButton.h"
@implementation ColorfulButton
@synthesize _highColor;
@synthesize _lowColor;
@synthesize gradientLayer;
- (void)awakeFromNib;
{
// Initialize the gradient layer
gradientLayer = [[CAGradientLayer alloc] init];
// Set its bounds to be the same of its parent
[gradientLayer setBounds:[self bounds]];
// Center the layer inside the parent layer
[gradientLayer setPosition:
CGPointMake([self bounds].size.width/2,
[self bounds].size.height/2)];
// Insert the layer at position zero to make sure the
// text of the button is not obscured
[[self layer] insertSublayer:gradientLayer atIndex:0];
// Set the layer's corner radius
[[self layer] setCornerRadius:8.0f];
// Turn on masking
[[self layer] setMasksToBounds:YES];
// Display a border around the button
// with a 1.0 pixel width
[[self layer] setBorderWidth:1.0f];
}
- (void)drawRect:(CGRect)rect;
{
if (_highColor && _lowColor)
{
// Set the colors for the gradient to the
// two colors specified for high and low
[gradientLayer setColors:
[NSArray arrayWithObjects:
(id)[_highColor CGColor],
(id)[_lowColor CGColor], nil]];
}
[super drawRect:rect];
}
- (void)setHighColor:(UIColor*)color;
{
// Set the high color and repaint
[self set_highColor:color];
[[self layer] setNeedsDisplay];
}
- (void)setLowColor:(UIColor*)color;
{
// Set the low color and repaint
[self set_lowColor:color];
[[self layer] setNeedsDisplay];
}
- (void)dealloc {
// Release our gradient layer
[gradientLayer release];
[super dealloc];
}
@end
Now, when I’m creating a button in Interface Builder, I can make it of classColorfulButton and then set an outlet for it in my view controller.
If I don’t set colors for a gradient, it will just render the button using the background color I specify for it in Interface Builder. If, however, I want to take advantage of the gradient capability, I set the gradient colors in my view controller as shown in the following code snippet:
These are the first four buttons you see in the demo app screenshot above. The buttons are declared in the view controller header as show in the following snippet:
The CAGradientLayer supports adding an array of colors and will automatically render them linearly with equal distribution. It will also allow you to specify the distribution pattern, however, to keep it simple here I just use two colors represented by the upper color called highColor and the lower color called lowColor. If you want to add more complex gradients, you could modify my ColorfulButtonclass to take an array of colors as well. I leave this as an exercise for the reader.
Conclusion
Next time you need to customize a button, consider using Core Animation layers first. You may be surprised at what all you can achieve without having to resort to creating images to use as the button background. I have included the source code for this blog post below. Let me know if you have any thoughts or questions about it in the comments section. Until next time.
Recent Comments