Code Snippet of the Week: Using Quartz to draw the background of UIButtons

Ever wanted to use UIButtons with backgrounds that change when a UIButton is pressed? Unfortunately, this is not so straightforward as one could wish for. Typically, you have to use predefined images to do this、 and set the background image according the state of the UIButton with
[contactButton setBackgroundImage: imageHighlighted forState:UIControlStateHighlighted];
A standard solution that includes that a PNG UIImage is displayed when the button is pressed looks like this:

UIImage *imageHighlighted = [UIImage imageNamed:@"highlighted.png"];
UIButton *contactButton = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, 158, 30)];
contactButton.backgroundColor = [UIColor clearColor];
[contactButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal];
[contactButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateHighlighted];
[contactButton setTitle:@"Contacts" forState:UIControlStateNormal];
[contactButton setTitle:@"Contacts" forState:UIControlStateHighlighted];
[contactButton actionsForTarget:@selector(selectContacts:) forControlEvent:UIControlEventTouchUpInside];
contactButton.titleLabel.font = [UIFont fontWithName:@"Verdana" size:12];
[contactButton setBackgroundColor:[UIColor clearColor]];
[contactButton setBackgroundImage: imageHighlighted forState:UIControlStateHighlighted];
[self addSubview:contactButton];

Wouldn’t it be nice to do this without PNG images you have to draw upfront? Fortunately, there is Quartz to help you with that. The main benefit of Quartz is its flexibility: you can apply all kind of effects on the UIButtons without needing to draw PNG images before. The Code for creating Quartz background images for UIButtons is straightforward: you have to define a context with UIGraphicsBeginImageContext from which you retrieve a UIImage with UIGraphicsGetImageFromCurrentImageContext. This image is later used in the button when it is highlighted: [contactButton setBackgroundImage: imageView.image forState:UIControlStateHighlighted];

// 1. create UIImageView container that contains a "nil" UIImage and assign variable for later use
UIImageView *imageView = [[UIImageView alloc] initWithImage:nil];
UIImage *image = imageView.image;
// 2. set size of the UIImageView -> the same as the UIButton
imageView.frame = CGRectMake(0, 0, 158, 30);
// 3. start image context
UIGraphicsBeginImageContext(CGSizeMake(158, 30));
// 4. define fill color of rectangle
CGContextSetFillColor(UIGraphicsGetCurrentContext(), CGColorGetComponents([UIColor colorWithRed:1 green:1 blue:1 alpha:1].CGColor));
// 5. draw rectangle
[image drawInRect:CGRectMake(0, 0, 158, 30)];
// 6. create context path
// 7. set origin
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), 0, 0);
// 8. add rectanlge to path
CGContextAddRect(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, 158, 30));
// 9. fill rectangle
// 10. get image from current context
image = UIGraphicsGetImageFromCurrentImageContext();

The trick is to use a UIImageView with an empty image: [[UIImageView alloc] initWithImage:nil]; This allows us later to access the UIImage from the UIImageView. The reason for this is that you cannot create an empty UIImage (at least, I’m not aware of this). The use is simple: you create a UIButton as you are used to and set the image for the state: [contactButton setBackgroundImage: image forState:UIControlStateHighlighted];

// 11. create UIButton
UIButton *contactButton = [[UIButton alloc]initWithFrame:CGRectMake(84, 225, 158, 30)];
contactButton.backgroundColor = [UIColor greenColor];
[contactButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal];
[contactButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateHighlighted];
[contactButton setTitle:@"Press Me" forState:UIControlStateNormal];
[contactButton setTitle:@"Color Change" forState:UIControlStateHighlighted];
contactButton.titleLabel.font = [UIFont fontWithName:@"Verdana" size:12];
// 12. set background image for highlighted state
[contactButton setBackgroundImage:image forState:UIControlStateHighlighted];
[self.view addSubview:contactButton];
[imageView release];
[contactButton release];

You can download the demo project from here.

Your coding ikangai team

PS: The code snippet uses portions of this code.

, , ,
  • Delicious
  • Facebook
  • Digg
  • Reddit
  • StumbleUpon
  • Twitter

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>