Back Buttons

Today, a quick note about “back” buttons. The chevron-shaped button that appears by default in the left-hand position of a nav. bar can be a little problematic. In particular, the system-provided button doesn’t cross-fade properly with other buttons, and it can be tricky to change its text programmatically. Furthermore, there doesn’t seem to be any way to (simply) generate your own chevron-shaped buttons. To address these issues, I created a UIButton category, presented below. This category allows you to create your own chevron-shaped buttons.

Problems

Let me first quickly explain the problems we’re trying to solve. First: cross-fade. Sometimes you want to swap out the buttons on a nav. bar with a fade animation. You might use the UINavigationController's setLeftBarButtonItem:animated: method to do this. Unfortunately, this method doesn’t do a proper cross-fade when a back button is involved; the back button tends to “pop”. Using setHidesBackButton:animated: doesn’t really help, unless you’re willing to schedule two animations.

A related problem has to do with relabeling the back button. For instance, in our Demine project, the back button is relabeled from “Pause” to “Menu” when a puzzle is completed. It’s not clear how you’d do this programmatically, since:

  • It’s not easy to get a pointer to the system-created back button
  • That button is owned by another controller’s UINavigationItem, and shared among all its “children”
  • The UIBarItem documentation states that a title should be set before a bar item is added to a nav. bar

All in all, it seems like it’s necessary to create your own chevron-shaped buttons in some cases, and it doesn’t seem like the system provides a way to do this.

Art

We’ll need some art. Here, experimentally determined, are appropriate images for the clicked and normal states of the back button.

Clicked

Normal

Code

Now, the code. First, we’ll need to declare a category in a header file:

@interface UIButton (NavButton)

- (UIButton*)configureForBackButtonWithTitle:(NSString*)title target:(id)target action:(SEL)action;

@end

And then, we’ll need to define the new method in a source file:

- (UIButton*)configureForBackButtonWithTitle:(NSString*)title target:(id)target action:(SEL)action
{
	// Experimentally determined
	CGFloat padTRL[3] = {6, 8, 12};

	// Text must be put in its own UIView, s.t. it can be positioned to mimic system buttons
	UILabel* label = [[UILabel alloc] init];
	label.backgroundColor = [UIColor clearColor];
	label.font = [UIFont boldSystemFontOfSize:12];
	label.textColor = [UIColor whiteColor];
	label.shadowColor = [UIColor darkGrayColor];
	label.shadowOffset = CGSizeMake(0, -1);
	label.text = title;
	[label sizeToFit];

	// The underlying art files must be added to the project
	UIImage* norm = [[UIImage imageNamed:@"back_norm.png"] stretchableImageWithLeftCapWidth:13 topCapHeight:0];
	UIImage* click = [[UIImage imageNamed:@"back_click.png"] stretchableImageWithLeftCapWidth:13 topCapHeight:0];
	[self setBackgroundImage:norm forState:UIControlStateNormal];
	[self setBackgroundImage:click forState:UIControlStateHighlighted];
	[self addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];

	// Calculate dimensions
	CGSize labelSize = label.frame.size;
	CGFloat controlWidth = labelSize.width+padTRL[1]+padTRL[2];
	controlWidth = controlWidth>=norm.size.width?controlWidth:norm.size.width;

	// Assemble and size the views
	self.frame = CGRectMake(0, 0, controlWidth, 30);
	[self addSubview:label];
	label.frame = CGRectMake(padTRL[2], padTRL[0], labelSize.width, labelSize.height);

	// Clean up
	[label release];
	return self;
}

Invocation

To create a new button with this code, you might write something like this:

[[UIBarButtonItem alloc] initWithCustomView:[[UIButton buttonWithType:UIButtonTypeCustom] configureForBackButtonWithTitle:@"Menu" target:self action:@selector(dismiss)]];

Caveats

There might be an easier way to do this, but I can’t see it right now. This approach does represent a bit of a hack; if AAPL changes the appearance of navigation bars/back buttons in future releases of iPhone OS, the buttons produced by this method won’t look right. (I guess you’d need new code/art that matches the new system look and, at least transitionally, introspective code that figures out the system version and invokes the appropriate code with the appropriate art.) Ah well. Life: Still imperfect.

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.