65

I would like to make a custom UIBarButtonItem that contains both image and text, something like this:

enter image description here

I tried subclassing UIBarButtonItem and overriding this method:

- (UIView *)customView
{
    if (!self.storedView) {

        UIView *temp = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 120, 44)];
        UIImageView *tempImageView = [[UIImageView alloc] initWithImage:self.image];
        tempImageView.frame = CGRectMake(0, 0, self.image.size.width, self.image.size.height);
        UILabel *tempLabel = [[UILabel alloc] initWithFrame:CGRectMake(44, 0, 100, 44)];
        tempLabel.text = @"text";
        [temp addSubview:tempImageView];
        [temp addSubview:tempLabel];
        self.storedView = temp;
    }
    return self.storedView;
}

And I use it like this:

UIBarButtonItem *left = [[LeftItem alloc] initWithTitle:@"Settings" style:UIBarButtonItemStylePlain target:self action:@selector(settingsPressed)];
left.title = @"Settings";
left.image = [UIImage imageNamed:@"settings.png"];
self.navigationItem.leftBarButtonItem = left;

But using this I only get the image, but not label. What am I doing wrong?

Dvole
  • 5,725
  • 10
  • 54
  • 87

11 Answers11

102
UIButton *button =  [UIButton buttonWithType:UIButtonTypeCustom];
[button setImage:[UIImage imageNamed:@"image.png"] forState:UIControlStateNormal];
[button addTarget:target action:@selector(buttonAction:)forControlEvents:UIControlEventTouchUpInside];
[button setFrame:CGRectMake(0, 0, 53, 31)];
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(3, 5, 50, 20)];
[label setFont:[UIFont fontWithName:@"Arial-BoldMT" size:13]];
[label setText:title];
label.textAlignment = UITextAlignmentCenter;
[label setTextColor:[UIColor whiteColor]];
[label setBackgroundColor:[UIColor clearColor]];
[button addSubview:label];
UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithCustomView:button];
self.navigationItem.leftBarButtonItem = barButton;
rounak
  • 9,217
  • 3
  • 42
  • 59
karthika
  • 4,085
  • 3
  • 21
  • 23
  • 4
    Worth noting that you can set the image to the buttons background image property, and that removes the need to add a label, because you can use the buttons label instead. – TigerCoding Mar 09 '14 at 17:46
  • 1
    Any suggestions how I would accomplish this in Interface Builder? I hate having parts of the UI in code and parts in IB. – oarfish Jun 03 '15 at 13:44
  • @oarfish write everything in code - problem solved :D That's most senior devs do it. – Palli May 18 '21 at 14:57
  • Unable to set tintColor to button with this approach any thoughts on setting tintColor? – iOSDude Sep 29 '21 at 19:52
23

You can add a custom view to the UIBarButtonItem.

In iOS 7, there is a new buttonType called UIButtonTypeSystem for UIButton which serve your purpose. Try this,

UIView* leftButtonView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 110, 50)];

UIButton* leftButton = [UIButton buttonWithType:UIButtonTypeSystem];
leftButton.backgroundColor = [UIColor clearColor];
leftButton.frame = leftButtonView.frame;
[leftButton setImage:[UIImage imageNamed:<YourImageName>] forState:UIControlStateNormal];
[leftButton setTitle:@"YourTitle" forState:UIControlStateNormal];
leftButton.tintColor = [UIColor redColor]; //Your desired color.
leftButton.autoresizesSubviews = YES;
leftButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin;
[leftButton addTarget:self action:@selector(<YourTargetMethod>) forControlEvents:UIControlEventTouchUpInside];
[leftButtonView addSubview:leftButton];

UIBarButtonItem* leftBarButton = [[UIBarButtonItem alloc]initWithCustomView:leftButtonView];
self.navigationItem.leftBarButtonItem = leftBarButton;

Updated Swift code,

let leftButtonView = UIView.init(frame: CGRect(x: 0, y: 0, width: 110, height: 50))

let leftButton = UIButton.init(type: .system)
leftButton.backgroundColor = .clear
leftButton.frame = leftButtonView.frame
leftButton.setImage(UIImage.init(imageLiteralResourceName: <YourImageName>), for: .normal)
leftButton.setTitle("YourTitle", for: .normal)
leftButton.tintColor = .red //Your desired color.
leftButton.autoresizesSubviews = true
leftButton.autoresizingMask = [.flexibleWidth , .flexibleHeight]
leftButton.addTarget(self, action: #selector(<YourTargetMethod>), for: .touchUpInside)
leftButtonView.addSubview(leftButton)

let leftBarButton = UIBarButtonItem.init(customView: leftButtonView)
navigationItem.leftBarButtonItem = leftBarButton
Ram Gandhi
  • 707
  • 8
  • 24
  • The question is, what should be the image's dimensions to look exactly like the native back image? (I want to create my own custom back button so I can control its events...) – Gal Mar 31 '14 at 14:44
  • This works when assign with: "self.navigationItem.leftBarButtonItem = leftBarButton;" but it doesnt when assign it to UIBarbuttonItem like "self.boto_icona_client = leftBarButton;" any idea why? – MiQUEL Apr 28 '14 at 01:37
14

If your BarButtonItem is in Storyboard, you can drag another Button into BarButtonItem, so there will be a Button inside the BarButtonItem, you can add both image and label to the button.

水清木华
  • 289
  • 4
  • 10
12

a SWIFT version

    let button =  UIButton(type: .Custom)
    button.setImage(UIImage(named: "icon_right"), forState: .Normal)
        button.addTarget(self, action: "buttonAction", forControlEvents: .TouchUpInside)
    button.frame = CGRectMake(0, 0, 53, 31)
    button.imageEdgeInsets = UIEdgeInsetsMake(-1, 32, 1, -32)//move image to the right
    let label = UILabel(frame: CGRectMake(3, 5, 50, 20))
    label.font = UIFont(name: "Arial-BoldMT", size: 16)
    label.text = "title"
    label.textAlignment = .Center
    label.textColor = UIColor.whiteColor()
    label.backgroundColor =   UIColor.clearColor()
    button.addSubview(label)
    let barButton = UIBarButtonItem(customView: button)
    self.navigationItem.rightBarButtonItem = barButton
drpawelo
  • 2,348
  • 23
  • 17
5

Using UIButton as custom view inside your UIBarButtonItem. You can create UIButton however you want, including with an image and text, using -setImage:forState: and -setTitle:forState:

UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setImage:[UIImage imageNamed:@"settings.png"] forState:UIControlStateNormal];
[button setTitle:@"Settings" forState:UIControlStateNormal];
[button addTarget:self action:@selector(buttonAction:)forControlEvents:UIControlEventTouchUpInside];
[button sizeToFit];
UIBarButtonItem* barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
self.navigationItem.leftBarButtonItem = barButtonItem;

this can be done using interface builder just by dragging a UIButton to the left bar button slot on a navigation bar.

Code cracker
  • 3,105
  • 6
  • 37
  • 67
sKhan
  • 9,694
  • 16
  • 55
  • 53
4

The problem with the accepted answer is that the UIBarButtonItem text does not change when highlighted. The code below will display text plus an image. The UIEdgeInsetsMake moves the text to the left and the image to the right.

UIButton *button =  [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self action:@selector(myButtonEvent:) 
             forControlEvents:UIControlEventTouchUpInside];
[button setFrame:CGRectMake(0, 0, 53, 32)];

[button setTitle:title forState:UIControlStateNormal];
[button setTitleColor:[UIColor whiteColor] 
                 forState:UIControlStateNormal];
[button setTitleColor:[UIColor lightGrayColor] 
                 forState:UIControlStateHighlighted];
button.titleEdgeInsets = UIEdgeInsetsMake(-2, -20, 2, 20);
button.titleLabel.font = font;
button.titleLabel.textAlignment = NSTextAlignmentLeft;

UIImage *image = [UIImage imageNamed:@"imageName"];
[button setImage:image 
        forState:UIControlStateNormal];
button.imageEdgeInsets = UIEdgeInsetsMake(-1, 32, 1, -32);
UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithCustomView:button];
bbarnhart
  • 6,620
  • 1
  • 40
  • 60
3

I posted answer that handles tint colour and image simmilar to normal uibarbuttonitem here... https://stackoverflow.com/a/28348461/925135

Community
  • 1
  • 1
Renetik
  • 5,887
  • 1
  • 47
  • 66
3

Another approach with NSAttributedString:

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@"Button Text"];
NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];
textAttachment.image = [UIImage imageNamed:@"buttonImage"];
textAttachment.bounds = CGRectMake(0, -3, textAttachment.image.size.width, textAttachment.image.size.height); //the origin y value depends on the size of the image to get a perfect fit
NSAttributedString *attrStringWithImage = [NSAttributedString attributedStringWithAttachment:textAttachment];
[attributedString replaceCharactersInRange:NSMakeRange(0, 0) withAttributedString:attrStringWithImage]; // Adding the image at the beginning
[customButton setAttributedTitle:attributedString forState:UIControlStateNormal];
[customButton sizeToFit];
[customButton addTarget:self action:@selector(back:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *back = [[UIBarButtonItem alloc] initWithCustomView:customButton];
Gabriel Candia
  • 282
  • 4
  • 14
3

Here's my solution based on previous answers. May be will be helpful.

extension UIBarButtonItem {
    static func button(image: UIImage, title: String, target: Any, action: Selector) -> UIBarButtonItem {
        let button = UIButton()
        button.setImage(image, for: .normal)
        button.addTarget(target, action: action, for: .touchUpInside)
        button.setTitle(title, for: .normal)
        button.sizeToFit()
        return UIBarButtonItem(customView: button)
    }
}
anatoliy_v
  • 1,782
  • 15
  • 24
2
UIImage *image = [UIImage imageNamed:@"icon.png"];
UIImage *backgroundSelected = [UIImage imageNamed:@"icon_selected.png"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self action:@selector(ButtonTapped:event:)forControlEvents:UIControlEventTouchUpInside]; //adding action
[button setBackgroundImage:image forState:UIControlStateNormal];
[button setBackgroundImage:backgroundSelected forState:UIControlStateSelected];
button.frame = CGRectMake(0 ,0,35,35);

then make that button as your custom barbutton

UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithCustomView:button];
self.navigationItem.leftBarButtonItem = barButton;
mrunmaya
  • 41
  • 6
2

This is the updated version of ripegooseberry's answer for Swift 3

    let button =  UIButton(type: .custom)
    button.setImage(UIImage(named: "icon_right"), for: .normal)
    button.addTarget(self, action: Selector(("buttonAction")), for: .touchUpInside)
    button.frame = CGRect(x: 0, y: 0, width: 53, height: 31)
    button.imageEdgeInsets = UIEdgeInsetsMake(-1, 32, 1, -32)//move image to the right
    let label = UILabel(frame: CGRect(x: 3, y: 5, width: 50, height: 20))
    label.font = UIFont(name: "Arial-BoldMT", size: 16)
    label.text = "title"
    label.textAlignment = .center
    label.textColor = UIColor.white
    label.backgroundColor =   UIColor.clear
    button.addSubview(label)
    let barButton = UIBarButtonItem(customView: button)
    self.navigationItem.rightBarButtonItem = barButton
JipZipJib
  • 81
  • 3
  • 5