Shiny Red Buttons (Digression)

Editorial Note: I was planning to discuss the gloss gradient code in detail today, with an eye to showing how some of its more arbitrary elements could be adjusted to alter the appearance of the gradients that it produces. Unfortunately, that post is running a day late, so instead I’m going to talk about a different way to hack up shiny buttons.

There is a way to create shiny buttons built in to the iPhone API, aside from the unsupported UIGlassButton class. The segmented control class (UISegmentedControl) will render widgets of the UISegmentedControlStyleBar control style with a “glossy” appearance. There are certain drawbacks, of course.

General Approach

Here’s how you’d typically use a segmented control:

// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView
{
	// TL view
	self.view = [[UIView alloc] initWithFrame:CGRectZero];
	self.view.backgroundColor = [UIColor whiteColor];
	
	UISegmentedControl* sc = [[[UISegmentedControl alloc] initWithFrame:CGRectMake(110, 12, 100, 24)] autorelease];
	[sc insertSegmentWithTitle:@"Foo" atIndex:0 animated:NO];
	[sc insertSegmentWithTitle:@"Bar" atIndex:1 animated:NO];
	[sc insertSegmentWithTitle:@"Baz" atIndex:2 animated:NO];
	sc.tintColor = [UIColor colorWithRed:0.7 green:0.0 blue:0.0 alpha:1];
	sc.segmentedControlStyle = UISegmentedControlStyleBar;
	[self.view addSubview:sc];
}

If you wanted to use this class to create a single large buttons, the code would look like this:

- (void)loadView
{
	// TL view
	self.view = [[UIView alloc] initWithFrame:CGRectZero];
	self.view.backgroundColor = [UIColor whiteColor];
	
	UISegmentedControl* sc = [[[UISegmentedControl alloc] initWithFrame:CGRectMake(5, 27, 310, 54)] autorelease];
	[sc insertSegmentWithTitle:@"Big Button" atIndex:0 animated:NO];
	sc.tintColor = [UIColor colorWithRed:0.7 green:0.0 blue:0.0 alpha:1];
	sc.segmentedControlStyle = UISegmentedControlStyleBar;
	[self.view addSubview:sc];
}

Unfortunately, there are a few problems:

  • You can’t control the font or size of the button text.
  • This button doesn’t act like a “normal” button in terms of highlighting, etc.
  • Bar buttons look sort of iffy blown up to this scale, and standing alone.

These problems can all be addressed, to greater or lesser degrees.

Text

The problem of text appearance can be solved pretty comprehensively, albeit at the cost of some effort. Briefly, the solution is to render text into an image, and then paste that image onto the button. The code to do so might look like this:

- (void)loadView
{
	// TL view
	self.view = [[UIView alloc] initWithFrame:CGRectZero];
	self.view.backgroundColor = [UIColor whiteColor];
	
	
	// Text bounds
	NSString*	text	= @"Big Button";
	UIFont*		font	= [UIFont boldSystemFontOfSize:18];
	CGSize		size	= [text sizeWithFont:font];
	
	// Rendering context
	UIGraphicsBeginImageContext(size);
	CGContextRef context = UIGraphicsGetCurrentContext();
	
	// Set colors
	CGContextSetFillColorWithColor(context, [[UIColor greenColor] CGColor]);
	CGContextSetStrokeColorWithColor(context, [[UIColor greenColor] CGColor]);
	
	// Render
	[text drawInRect:CGRectMake(0, 0, size.width, size.height) withFont:font];
	
	// Create image and release context
	UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
	UIGraphicsEndImageContext();
	
	// Segmented control
	UISegmentedControl* sc = [[[UISegmentedControl alloc] initWithFrame:CGRectMake(5, 27, 310, 54)] autorelease];
	[sc insertSegmentWithImage:image atIndex:0 animated:NO];
	sc.tintColor = [UIColor colorWithRed:0.7 green:0.0 blue:0.0 alpha:1];
	sc.segmentedControlStyle = UISegmentedControlStyleBar;
	[self.view addSubview:sc];
}

Behaviour

The quickest way to make a UISegmentedControl act sort of like a normal button is to set its momentary property to YES. If you subclassed it, you might be able to do better, but I’m only interested in presenting a quick hack here.

Appearance

Since we’re getting a “glossy” appearance by using an image that’s supposed to be inset into a bar (the segmentedControlStyle is UISegmentedControlStyleBar, after all) the image doesn’t really look right when standing free. As a quick fix, I find that a gray background looks better. Code that incorporates this fix, plus the behaviour fix discussed above, would look like this:

- (void)loadView
{
	// TL view
	self.view = [[UIView alloc] initWithFrame:CGRectZero];
	self.view.backgroundColor = [UIColor grayColor];
	
	
	// Text bounds
	NSString*	text	= @"Big Button";
	UIFont*		font	= [UIFont boldSystemFontOfSize:18];
	CGSize		size	= [text sizeWithFont:font];
	
	// Rendering context
	UIGraphicsBeginImageContext(size);
	CGContextRef context = UIGraphicsGetCurrentContext();
	
	// Set colors
	CGContextSetFillColorWithColor(context, [[UIColor greenColor] CGColor]);
	CGContextSetStrokeColorWithColor(context, [[UIColor greenColor] CGColor]);
	
	// Render
	[text drawInRect:CGRectMake(0, 0, size.width, size.height) withFont:font];
	
	// Create image and release context
	UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
	UIGraphicsEndImageContext();
	
	// Segmented control
	UISegmentedControl* sc = [[[UISegmentedControl alloc] initWithFrame:CGRectMake(5, 27, 310, 54)] autorelease];
	[sc insertSegmentWithImage:image atIndex:0 animated:NO];
	sc.tintColor = [UIColor colorWithRed:0.7 green:0.0 blue:0.0 alpha:1];
	sc.segmentedControlStyle = UISegmentedControlStyleBar;
	sc.momentary = YES;
	[self.view addSubview:sc];
}

Test resultsIt is a hack, but perhaps you will find it useful. It’s a lot simpler to get working that the roll-your-own glossy buttons we’ve been building, and it’s not quasi-legal like UIGlassButton.

Share and Enjoy:
  • Twitter
  • Facebook
  • Digg
  • Reddit
  • HackerNews
  • del.icio.us
  • Google Bookmarks
  • Slashdot
This entry was posted in iPhone. Bookmark the permalink.

Comments are closed.