16

I know how to customize UIBarButtonItem using -setBackgroundImage: forState: barMetrics:, but I would like to use different images for UIBarButtonItemStyleDone and UIBarButtonItemStylePlain.

Is there a way to accomplish this using the UIAppearance protocol? Or do I have to set the image each time I want a "Done" style button?

(I tried messing around with code like the following:

[[UIBarButtonItem appearance] setBackgroundImage:image forState:UIControlStateNormal barMetrics:UIBarButtonItemStyleDone];

But that just sets every bar button with the "Done" image.)

Thanks!

James
  • 762
  • 8
  • 22
  • Do u want to change the back image on button click? – AppleDelegate Sep 25 '12 at 13:14
  • I've customized the back button already. I'm looking to have different "normal" bar buttons (like how iOS uses a blue button for "Done" and "Save" but gray for things like "Cancel"). – James Sep 25 '12 at 13:40
  • @James, One option is to use a custom subclass for Done button as `CustomDoneBarButtonItem` from `UIBarButtonItem` and use it as `[[CustomDoneBarButtonItem appearance] setBackgroundImage:image forState:UIControlStateNormal barMetrics:UIBarButtonItemStyleDone]`. Whenever you are adding a done button, create an object of this custom class and add it. – iDev Dec 05 '12 at 00:43
  • This does not work, because when you call `appearance` on the subclass, it calls `appearance` on the superclass, and all your UIBarButtonItem's get this style. See this thread: http://stackoverflow.com/questions/11022468/how-to-use-uiappearance-to-style-a-subclass-but-not-the-superclass – Danny Jan 16 '13 at 18:21

2 Answers2

13

In iOS 6 you can use the new method of UIBarButtonItem class:

- (void)setBackgroundImage:(UIImage *)backgroundImage
                  forState:(UIControlState)state
                     style:(UIBarButtonItemStyle)style
                barMetrics:(UIBarMetrics)barMetrics

It sets the background image for the specified state, style, and metrics. More details are available in the Apple docs

So to change the appearance of all UIBarButtonItems you can use something like:

UIImage *doneBackgroundImage = [[UIImage imageNamed:@"button_done.png"]
   resizableImageWithCapInsets:UIEdgeInsetsMake(0, 4, 0, 4)];

[[UIBarButtonItem appearance] setBackgroundImage:doneBackgroundImage
                                            forState:UIControlStateNormal
                                               style:UIBarButtonItemStyleDone
                                          barMetrics:UIBarMetricsDefault];
Daij-Djan
  • 49,552
  • 17
  • 113
  • 135
Sergiy Salyuk
  • 325
  • 2
  • 7
  • 2
    it is official API, available in iOS 6 only (which I stated explicitly). It solves exactly the problem described: "way to accomplish this [different images for UIBarButtonItemStyleDone and UIBarButtonItemStylePlain] using the UIAppearance protocol". – Sergiy Salyuk Dec 04 '12 at 10:27
  • yes sorry the downvote doesnt make sense did read it wrong - totally my bad! correct. thanks for that :) i need ios5 though... else it would be easy – Daij-Djan Dec 04 '12 at 10:39
  • 1
    No problem, I'm also looking for the good solution for iOS 5 ;-) – Sergiy Salyuk Dec 04 '12 at 19:24
  • I was looking for a solution for iOS 5 as well when I asked the question, but since it's not specifically stated, I'll accept this answer. – James Dec 05 '12 at 02:45
  • 1
    Any ideas how to customize the text title attributes for UIarButtonItemStyleDone? setTitleTextAttributes:forState: is missing the style parameter;( – Klaas May 14 '13 at 22:14
10

for IOS5*

The only way I found is using a UIBarButtonItem category:

UIBarButtonItem+Appearance.h

#import <Foundation/Foundation.h>

@interface UIBarButtonItem (Appearance)

+ (void) setupAppearance;

@end

UIBarButtonItem+Appearance.m

#import "UIBarButtonItem+Appearance.h"
#import <objc/runtime.h>

@implementation UIBarButtonItem (Appearance)

+ (void) setupAppearance {
    [[UIBarButtonItem appearance]  setBackgroundImage: [[UIImage imageNamed:@"customButton"]
                                                                  resizableImageWithCapInsets: UIEdgeInsetsMake(8, 8, 8, 8)]
                                                       forState: UIControlStateNormal
                                                     barMetrics: UIBarMetricsDefault];

    [[UIBarButtonItem appearance]  setBackgroundImage: [[UIImage imageNamed:@"customButtonHiglhighted"]
                                                                  resizableImageWithCapInsets: UIEdgeInsetsMake(8, 8, 8, 8)]
                                                       forState: UIControlStateHighlighted
                                                     barMetrics: UIBarMetricsDefault];



    Class klass = objc_getClass("UIBarButtonItem");
    Method targetMethod = class_getInstanceMethod(klass, @selector(setStyle:));
    Method newMethod = class_getInstanceMethod(klass, @selector(__setStyle:));
    method_exchangeImplementations(targetMethod, newMethod);
}

- (void) __setStyle:(UIBarButtonItemStyle)style {
    [self __setStyle:style];

    if(style == UIBarButtonItemStyleDone) {
        [self setBackgroundImage:[[UIImage imageNamed:@"customDoneButton"] resizableImageWithCapInsets: UIEdgeInsetsMake(8, 8, 8, 8)]
                        forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
        [self setBackgroundImage:[UIImage imageNamed:@"customDoneButtonClicked"]
                        forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
    } else {
        [self setBackgroundImage:[[UIImage imageNamed:@"customButton"] resizableImageWithCapInsets: UIEdgeInsetsMake(8, 8, 8, 8)]
                        forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
        [self setBackgroundImage:[UIImage imageNamed:@"customButtonHighlighted"]
                        forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
    }
}

@end

Hope this is what you are looking for. Credit for this solution goes to https://gist.github.com/2633081

Daij-Djan
  • 49,552
  • 17
  • 113
  • 135
Molotoff
  • 553
  • 5
  • 7
  • 1
    Is this [MethodSwizzling](http://cocoadev.com/wiki/MethodSwizzling)? If yes there is always risk of appstore rejection. – iDev Dec 05 '12 at 00:36
  • @acb yes it is, but I know many apps that do it and while this does in no way legalize this, I _feel_ its ok **in this case** – Daij-Djan Dec 07 '12 at 11:50
  • Unfortunately this does not seem to work with some system controllers, e.g. on MFMailComposeViewController. – proxi Jul 08 '13 at 09:06