There are a few tricks you need to learn if you want to change the style of your toolbar and the buttons on it. Xcode supports custom tints on the toolbar and toolbar buttons if you only want to re-color it. However, if you need to apply a new theme you'll need to use code to adjust the button images.
To help you get started, I've written a utility class that can customize the for a . If you need to customize a the concepts are similar, but there are slightly different method calls. Check the reference for details.
1. You can download the source code and sample project from github or ZIP.
2. Watch the
Steps to Toolbar Button Perfection
I want to share how the code works, so that it doesn't seem magical. After you start working with more code these types of things will become second nature. Let's write some code.
1. First we'll need an image to use as a button in normal and highlighted (pressed) states. On iPhone you'll want to use stretchable images, because button titles can be variable widths. You can create a stretchable square image in PhotoShop CS6. Toolbar buttons are 30x30 "points." On current retina screens it's 2x scale factor, which means you'll need a 60x60 pixel image. I use a rounded rectangle with a 10 pixel corner radius in PhotoShop for toolbar buttons.
a. Save your retina image (60x60) in PhotoShop with the @2x ending (i.e. ) and then you can export the file and add it to your Xcode project.
b. Tip: When you edit your custom button graphic you'll want to replace the existing file in the project directory instead of re-adding it to Xcode. Right-click on the image in Xcode's project navigator and choose "Show in Finder." Using this window copy and paste the .PNG image and replace the old image. Or you can directly export from your .PSD file into your project directory to replace the old asset.
2. Here are the button squares that we'll use as button graphics. I have a normal, highlighted, and a crosshair test image to show how stretching works.
3. In code we load the image into a UIImage, and then we'll need to make that image resizable to allow for different button widths. The method calls are nested together.
UIImage *buttonNormal = [[UIImage imageNamed:@"StretchButton.png"]
resizableImageWithCapInsets:UIEdgeInsetsMake(15, 15, 15, 15)
resizingMode:UIImageResizingModeStretch];
UIImage *buttonHighlighted = [[UIImage imageNamed:@"StretchButton-Highlighted.png"]
resizableImageWithCapInsets:UIEdgeInsetsMake(15, 15, 15, 15)
resizingMode:UIImageResizingModeStretch];
a. I'm only adding the "" (retina) image for the button because Xcode/iPhone will auto-scale for non-retina devices. When we load the image, use the "StretchButton.png" to get this auto-scale behavior. Note: Unless you want pixel perfection on a non-retina (iPhone 3GS, iPad 2, or iPad Mini) you shouldn't need to add two versions of the same image.
b. The UIEdgeInsets technically only needs to be set for the left and right, but I like to set all my edges to the same value. In the above code (15, 15, 15, 15) corresponds to the sides (top, left, bottom, right). This tells the iPhone to move 15 pixels in on each side and then to stretch the pixels in the middle region (we strive for a 1 pixel row/column on one of the axises).
c. Tip: When creating the art asset, you'll want to make sure that your insets account for any corners or non-stretchable regions. If you don't, you'll get weird effects when the image stretches.
d. If you create your image correctly, the toolbar button will only stretch horizontally. If you work with UIButtons, they can be resized both horizontally and vertically. Be consistent with your insets and your button images can work in both situations (UIButtons have a 14 pixel corner radius).
e. Use UIImageResizingModeStretch for optimal performance. If you have 6+ buttons or use resizable images in a UITableView you'll notice lag.
4. In order to set a UIBarButtonItem to a custom image, we need to create a custom UIView and put it inside the UIBarButtonItem. To get the same tap behavior, we'll use a UIButton as our custom UIView.
a. We're responsible for setting both a normal and a highlighted state, which means you'll need two different images to provide visual feedback. The highlighted state is when a user touches the button.
b. To utilize the button titles from interface builder, we'll use the same title and then we need to resize our custom button. Setting the contentEdgeInsets and the font size will give make sizeToFit resize correctly and match the default button style.
c. Adding a drop shadow makes the title text pop out a little more from the button graphic.
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setBackgroundImage:image forState:UIControlStateNormal];
[button setBackgroundImage:highlightedImage forState:UIControlStateHighlighted];
[button setTitle:barButtonItem.title forState:UIControlStateNormal];
[button setContentEdgeInsets:UIEdgeInsetsMake(0, 10, 0, 10)];
button.titleLabel.font = [UIFont boldSystemFontOfSize:12];
[button sizeToFit];
// Text shadow
button.titleLabel.shadowOffset = CGSizeMake(0,-1);
button.titleLabel.shadowColor = [UIColor darkGrayColor];
5. The last step is to copy the target/action from the UIBarButtonItem and set it on the UIButton object. The custom view will receive all the touch events and we want it to respond like we've set it up in interface builder.
[button addTarget:barButtonItem.target action:barButtonItem.action
forControlEvents:UIControlEventTouchUpInside];
6. I wrapped the toolbar customization code in a helper class as a class method, so that you can just import the Helper.h file and then use a single method to stylize your button.
a. Set IBOutlets to the buttons and the toolbar, so that you can reference each of them in code and modify their appearance before the app starts.
// Top of ViewController class
#import "Helper.h"
// Inside method: - (void)viewDidLoad
[Helper customizeBarButton:myButton
image:normalButtonImage
highlightedButtonImage:buttonHighlighted];
Custom Toolbar Image
hange the appearance of your UIToolbar to match the style of your new buttons using and IBOutlet to the toolbar and the method below. Use a stretchable image to resize when the iPhone is rotated, or you can set the landscape using the setting UIBarMetricsLandscapePhone.
UIImage *toolbarImage = [[UIImage imageNamed:@"toolbar.png"]
resizableImageWithCapInsets:UIEdgeInsetsMake(22, 22, 22, 22)
resizingMode:UIImageResizingModeStretch];
[self.toolbar setBackgroundImage:toolbarImage
forToolbarPosition:UIToolbarPositionAny
barMetrics:UIBarMetricsDefault];
Wrap-up
- Download the sample project and replace the image assets with your artwork.
- Subscribe to my iPhone newsletter.
- Checkout my beginner iPhone development course on Skillshare.