My iPhone view adds some custom buttons to its toolbar. Each button has both an image and textual title, and is created like this:
UIBarButtonItem *fooButton
I was trying to use the UIBarButtonItem to display both an image and a button, but I'm pretty sure it's locked down to display one or the other. Using the basic idea from this thread I came up with a solution using a UIButton and a background image. The biggest flaw of what I've done is that when the title text varies in size a lot, the background image stretches causing the rounded corners to look a little off.
CustomBarButtonItem.h
#import <UIKit/UIKit.h>
@interface CustomBarButtonItem : UIBarButtonItem {}
- (id) initWithImage:(UIImage *)image title:(NSString *)title target:(id)target action:(SEL)action;
@end
@interface UIBarButtonItem (CustomBarButtonItem)
+ (UIBarButtonItem *) barButtonItemWithImage:(UIImage *)image title:(NSString *)title target:(id)target action:(SEL)action;
@end
CustomBarButtonItem.m
#import "CustomBarButtonItem.h"
@implementation CustomBarButtonItem
- (id) initWithImage:(UIImage *)image title:(NSString *)title target:(id)target action:(SEL)action {
UIButton *barButton = [UIButton buttonWithType:UIButtonTypeCustom];
UIFont *font = [UIFont boldSystemFontOfSize:13];
barButton.titleLabel.font = font;
barButton.titleLabel.shadowOffset = CGSizeMake(0, -1);
barButton.titleEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 5);
[barButton setImage:image forState:UIControlStateNormal];
[barButton setTitle:title forState:UIControlStateNormal];
[barButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[barButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateHighlighted];
[barButton setTitleShadowColor:[[UIColor blackColor] colorWithAlphaComponent:0.5] forState:UIControlStateNormal];
[barButton setBackgroundImage:[UIImage imageNamed:@"bar-button-item-background.png"] forState:UIControlStateNormal];
barButton.frame = CGRectMake(0, 0, image.size.width + 15 + [title sizeWithFont:font].width, 30);
if (self = [super initWithCustomView:barButton]) {
self.target = target;
self.action = action;
}
return self;
}
@end
@implementation UIBarButtonItem (CustomBarButtonItem)
+ (UIBarButtonItem *) barButtonItemWithImage:(UIImage *)image title:(NSString *)title target:(id)target action:(SEL)action {
return [[[CustomBarButtonItem alloc] initWithImage:image title:title target:target action:action] autorelease];
}
@end
Sample usage:
UIBarButtonItem *customButtonItem = [UIBarButtonItem barButtonItemWithImage:[UIImage imageNamed:@"calendar.png"] title:@"Add to calendar" target:self action:@selector(addToCalendar)];
My background image for the button is:
Johnus's solution is pretty useful. But I've tried it and a little problem occured: the action was not sent to the bar button item, so I've modified the initializer a little :
- (id) initWithImage:(UIImage *)image title:(NSString *)title target:(id)target action:(SEL)action {
UIButton *barButton = [UIButton buttonWithType:UIButtonTypeCustom];
UIFont *font = [UIFont boldSystemFontOfSize:13];
barButton.titleLabel.font = font;
barButton.titleLabel.shadowOffset = CGSizeMake(0, -1);
barButton.titleEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 5);
[barButton setImage:image forState:UIControlStateNormal];
[barButton setTitle:title forState:UIControlStateNormal];
[barButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[barButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateHighlighted];
[barButton setTitleShadowColor:[[UIColor blackColor] colorWithAlphaComponent:0.5] forState:UIControlStateNormal];
[barButton setBackgroundImage:[UIImage imageNamed:@"bar-button-item-background.png"] forState:UIControlStateNormal];
barButton.frame = CGRectMake(0, 0, image.size.width + 15 + [title sizeWithFont:font].width, 30);
if (self = [super initWithCustomView:barButton]) {
self.target = target;
self.action = action;
// I've added just one line of code here
[barButton addTarget:target
action:action
forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
The answers here are nice, however its often a little easier to use composition rather than inheritance with UIKit classes.
Create a bar item like so:
_accountBarItem =
[[UIBarButtonItem alloc] initWithCustomView:[CustomerBarView buttonWithImage:
[UIImage imageNamed:@"Account.png"] text:@"Account"]];
[_accountBarItem setTarget:self];
[_accountBarItem setAction:@selector(accountButtonPressed)];
Implementation of Custom View with Image and Label
@implementation CustomBarView
+ (instancetype)buttonWithImage:(UIImage*)image text:(NSString*)text
{
CustomBarView* button = [[CustomBarView alloc] initWithFrame:CGRectMake(0, 0, 44, 44)];
[button setText:text];
[button setImage:image];
return button;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
_uiButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self addSubview:_uiButton];
_label = [[UILabel alloc] initWithFrame:CGRectZero];
[_label setTextColor:[UIColor whiteColor]];
[_label setFont:[UIFont boldApplicationFontOfSize:9]];
[_label setBackgroundColor:[UIColor clearColor]];
[_label setTextAlignment:NSTextAlignmentCenter];
[self addSubview:_label];
}
return self;
}
- (void)setText:(NSString*)text
{
[_label setText:text];
}
- (void)setImage:(UIImage*)image
{
[_uiButton setImage:image forState:UIControlStateNormal];
}
- (void)layoutSubviews
{
[super layoutSubviews];
[_uiButton setFrame:self.bounds];
[_label setFrame:CGRectMake(2, self.height - 13, self.width - 4, 15)];
}
@end