Adding any kinds of UI elements into a UINavigationBar

In iCab Mobile and many other iPhone apps you can see a toolbar with buttons, text fields and other UI elements at the top of the screen. And this toolbar looks like a UINavigationBar object. But if you try to add UI elements into a UINavigationBar object in Interface Builder, you’ll notice that you can’t add all UI elements in any location within the UINavigationBar. More concrete: you can add up to three elements in concrete spots (left, middle, right), and only certain UI elements. A UINavigationBar object is meant to be used for navigation purposes and therefore is usually automatically populated by a UINavigationController. And the UINavigationController only uses the three spots (left to go back to the previous navigation level, the center area to display the title and the right to add an “edit” button for example).

So how are iCab Mobile and other apps able to populate a UINavigationBar object with any UI elements in other than the three available spots?

First of all, not everything which looks like a UINavigationBar is a UINavigationBar. Up to iCab Mobile 1.7 the toolbar at the top of the screen is not a UINavigationBar object, it’s a UIImageView object with an image that looks like a UINavigationBar would look like. But since iCab Mobile 2.0 it will be a UINavigationBar. iCab Mobile 2.0 will allow to customize the colors of the toolbars, and unlike images, the UINavigationBar object can have any color.

But even for UIImageView objects, you can’t add any subviews in Interface Builder. So the main question is the same: how to add any available UI elements in any location as subview of these objects (it doesn’t matter if these objects are UIImageView or UINavigationBar objects)?

The answer is simple: you have to do this programatically. UIImageView and UINavigationBar are both subclasses of UIView. And UIView objects can have subviews. You only need to call the method addSubview: to add a subview. When using UINavigationBar as the root for our toolbar, we only have to make sure that it is not managed by a UINavigationController. But because we need to create all the objects programmatically, this isn’t a problem at all.

One place to create the toolbar can be the viewDidLoad method of the UIViewController which serves as the controller for the view that holds the toolbar. The following code is an example how this would look like. All the code fragments should go into the viewDidLoad method, I’ve split the code into fragments only because it’s easier to explain what I’m doing this way.

- (void)viewDidLoad
  [super viewDidLoad];

  CGFloat width = self.view.frame.size.width;

Here we first get the width of the view of our UIViewController. It is used to calculate the widths of all of the toolbar elements.

  UINavigationBar *navBar = [[UINavigationBar alloc] initWithFrame:
  navBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  [self.view addSubview:navBar];
  [navBar release];

This fragment creates the UINavigationBar element and we add it as subview to the view object of our UIViewController. If the application should support the autorotation feature, we have to make sure that the width is flexible and set the autoresizing mask accordingly.

  UILabel *label = [[UILabel alloc] initWithFrame:
  label.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  label.text = @"some text for the title...";
  label.backgroundColor = [UIColor clearColor];
  label.font = [UIFont systemFontOfSize:12];
  label.textAlignment = UITextAlignmentCenter;
  [navBar addSubview:label];
  [label release];

Then we create the title line of our toolbar. We use a UILabel element. The width must be flexible as well when the autorotation feature is supported. The label is added as a subview of the UINavigationBar object so that there is a small margin at the top, left and right side. It is important to set the background color to “clearColor” to make the background completely transparent. The default background color would be white.

  UITextField *textField = [[UITextField alloc] initWithFrame:
  textField.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  textField.borderStyle = UITextBorderStyleRoundedRect;
  textField.font = [UIFont systemFontOfSize:17];
  [navBar addSubview:textField];
  [textField release];

This fragment will add a text field to enter some text into the toolbar. This is similar to what we’ve done to add the label.

  UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
  [button setFrame:CGRectMake(width-60,19,50,26)];
  [button setTitle:@"Go" forState:UIControlStateNormal];
  button.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
  [navBar addSubview:button];

And finally we’ve added a button to the toolbar. Nothing special here. This time the autoresizing mask is set to have a flexible left margin, because the button should always have the same width. It only should be placed on the right side of the toolbar, so the right margin must be fixed, while the left must be flexible.

This is more or less all you need to do to add UI elements in UINavigationBar objects. Of course you also need to set the delegates for some of the UI elements in order to process clicks on the button or text field editing.

This is how the toolbar from above will look like: