Customizing UIBarButtonItem "Done" style and "Plain" style separately using UIAppearance - iphone

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!

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];

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

Related

How can I change images for UIBarButtonItems of UINavigationController in QLPreviewController

I'm using QLPreviewController for showing a pdf. I want to change the background images for UINavigationBar and UIBarButtonItems of QLPreviewController.
I'm using this code:
for (id object in controller.childViewControllers)
{
if ([object isKindOfClass:[UINavigationController class]])
{
UINavigationController *navController = object;
[navController.navigationBar setBackgroundImage:[UIImage imageNamed:#"navbar_1x.png"] forBarMetrics:UIBarMetricsDefault];
NSLog(#"items count ====%d",[[navController.navigationBar items] count]);
if ([[navController.navigationBar items] count] > 0)
{
UINavigationItem *navItem = [[navController.navigationBar items] objectAtIndex:0];
navItem.rightBarButtonItem.image = [UIImage imageNamed:#"pdf30.png"];
}
}
}
By using above code I changed background image of navigationbar. But I can't changing background images for barbuttonitems.
Please help me. Thanks in advance.....
If you want you can customize the bar button.. try like this..
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[backButton setImage:[UIImage imageNamed:#"pdf30.png"] forState:UIControlStateNormal];
[backButton addTarget:self action:#selector(didTapBackButton:) forControlEvents:UIControlEventTouchUpInside];
backButton.frame = CGRectMake(0.0f, 0.0f, 64.0f, 41.0f);
UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
self.navigationItem.leftBarButtonItem = backButtonItem;
}
UIImage*resizedImage = [[UIImage imageNamed:#"navbar_2x.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 12, 12, 10)];
id navbar =[UINavigationBar appearance];
id barbutton =[UIBarButtonItem appearance];
//this customises the navigation bar
[navbar setBackgroundImage:resizedImage forBarMetrics:UIBarMetricsDefault];
UIImage *backButton = [[UIImage imageNamed:#"backwhite.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(10 , 18, 10 , 18)];
// this customises the back bar button item in the navigation bar
[barbutton setBackButtonBackgroundImage:backButton forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
// this for other bar button items
[barbutton setBackgroundImage:backButton forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];

Distorted navigation bar custom back button

I have a custom back button and the button is not displaying as intended, it is being stretched, and even though the back button text is empty it is still displaying the "back"
text.
Thanks in advance.
I usually use this technique to make it work:
To get rid default title:
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(-400.f, 0) forBarMetrics:UIBarMetricsDefault];
Use resizable image:
UIImage *backButton = [UIImage imageNamed:#"back.png"];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:[backButton resizableImageWithCapInsets:UIEdgeInsetsMake(0, backButton.size.width, 0, 0)] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
Try something like
UIButton *back = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *image = ... your image
[back setBackgroundImage:image forState:UIControlStateNormal];
[back addTarget:self action:#selector(back) forControlEvents:UIControlEventTouchUpInside];
back.frame = CGRectMake(0, 0, image.size.width, image.size.height);
UIBarButtonItem *backbi = [[UIBarButtonItem alloc] initWithCustomView:back];
self.navigationItem.leftBarButtonItem = backbi;
or post your code.

Strange behavior of UIBarButtonItem when using UIAppearance

I'm customizing the elements like navigation bar, tab bar and uibarbuttonitem using UIAppearance. It works very well except for a very strange behavior of the UIBarButtonItem elements. On the top level of the navigation controller hierarchy everything looks good, but if I push the next view controller, the UIBarButtonItem elements move a little bit downwards, but at the same time, the back button stays at its correct position. I attached two images to illustrate my problem.
1) First view controller in the navigation controller hierarchy
2) Second view controller in the navigation controller hierarchy
EDIT: The code
//Change navigation bar appearance
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:#"GPNavigationBarBackground.png"] forBarMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setShadowImage:[[UIImage alloc] init]];
[[UINavigationBar appearance] setShadowImage:[UIImage imageNamed:#"GPNavigationBarShadow.png"]];
[[UINavigationBar appearance] setTitleTextAttributes:
[NSDictionary dictionaryWithObjectsAndKeys:
[UIColor whiteColor], UITextAttributeTextColor,
[UIFont boldSystemFontOfSize:17], UITextAttributeFont,nil]];
[[UINavigationBar appearance] setTitleVerticalPositionAdjustment:2.f forBarMetrics:UIBarMetricsDefault];
UIImage *buttonBackground = [[UIImage imageNamed:#"GPNavigationBarButton.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 10, 0, 10)];
UIImage *buttonPressedBackground = [[UIImage imageNamed:#"GPNavigationBarButtonPressed.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 10, 0, 10)];
UIImage *backButtonBackground = [[UIImage imageNamed:#"GPNavigationBarBackButton.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 10)];
UIImage *backButtonPressedBackground = [[UIImage imageNamed:#"GPNavigationBarBackButtonPressed.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 10)];
[[UIBarButtonItem appearance] setBackgroundImage:buttonBackground forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackgroundImage:buttonPressedBackground forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:backButtonBackground forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:backButtonPressedBackground forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackButtonBackgroundVerticalPositionAdjustment:1.f forBarMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackgroundVerticalPositionAdjustment:1.f forBarMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setTitlePositionAdjustment:UIOffsetMake(0, 1.f) forBarMetrics:UIBarMetricsDefault];
Well, after brooding on that problem, I finally solved it. The code above is absolutely correct. The only problem was the height of the background images for the UIBarButtonItem. The UIAppearance proxy lets you set all the graphics, but it doesn't allow you to alter the height of the UIBarButtonItem.
So, when customizing UIBarButtonItem, always remember that a
UIBarButtonItem can't be higher than 30pt. That means that custom artwork shouldn't exceed this size.
You can set background images that are higher than 30pt, but you will run into the same problem as stated above.

how to change the color of backBarButtonItem?

In my application I want to change the color of bacBarButtonItem.
Is it possible to change the color? or I have to put an image of it.
and in case of image tell me the code how to put the image.
If you just want to change the color you can do it with this line of code.
[[UIBarButtonItem appearance] setTintColor:[UIColor redColor]];
Replace redColor with the following to adjust the color of the buttons:
colorWithRed:0/255.0 green:144/255.0 blue:200/255.0 alpha:1.0// pick your color using this
Be sure to put this in the view controller that pushes. Not the view controller where you want to see this back button color.
Praveen-K answer is right, but take in mind that you'll have to do it in every viewcontroller.
Starting at iOS5, Apple have introduced the "appearance" concept.
- (void)setBackButtonBackgroundImage:(UIImage *)backgroundImage forState:(UIControlState)state barMetrics:(UIBarMetrics)barMetrics __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0) UI_APPEARANCE_SELECTOR;
In your case would be something like this
UIImage *image = [UIImage imageNamed:#"imageName.png"];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:image forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
But as I said, Praveen-K answer is ok and will work, but just to let you know for the future.
UIImage *image = [UIImage imageNamed:#"imageName.png"];
UIBarButtonItem* backBarButton = [[UIBarButtonItem alloc] initWithImage:image style:UIBarButtonItemStylePlain target:self action:#selector(backButtonAction)];
self.navigationItem.leftBarButtonItem=backBarButton;
[backBarButton release];
Another way to change the color of back bar button item is to use segment control
UISegmentedControl *button = [[[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:#"Back", nil]] autorelease];
button.frame = CGRectMake(0, 0, 60, 30);
button.center = self.view.center;
button.momentary = YES;
button.segmentedControlStyle = UISegmentedControlStyleBar;
button.tintColor = [UIColor colorWithRed:0 green:0.1 blue:0.5 alpha:0];
[button addTarget:self action:#selector(handleBack:) forControlEvents:UIControlEventValueChanged];
UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
Notice that we assign the color we want to the property tintColor of UISegmentedControl.
I got the idea from this site:
http://charles.lescampeurs.org/2011/02/10/tint-color-uibutton-and-uibarbuttonitem

iPhone custom UINavigationBar buttons

I have an application with 4 tabs. Each tab is a UINavigationController. The 4 UINavigationBar tabs should look the same, have a custom background image, a custom backButton and a custom right button triggering a function.
I would like to do those customizations only once in my code and not in each RootViewController.
I managed to have a custom background image by inserting this code into my appDelegate:
#implementation UINavigationBar (CustomImage)
- (void)drawRect:(CGRect)rect {
UIImage *image = [UIImage imageNamed: #"MyNavigationBar.png"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
#end
But I didn't manage to customize the back and right buttons or specify the action for the right button.
Is there a way to do that in the appDelegate, just like for the background image?
Or should I do the customization in each RootViewController?
Write the below code in viewWillAppear method
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *butImage = [[UIImage imageNamed:#"back.png"] stretchableImageWithLeftCapWidth:10 topCapHeight:10];
[button setBackgroundImage:butImage forState:UIControlStateNormal];
[button addTarget:self action:#selector(gotoBack:) forControlEvents:UIControlEventTouchUpInside];
button.frame = CGRectMake(0, 0, 48, 30);
UIBarButtonItem *backButton = [[[UIBarButtonItem alloc] initWithCustomView:button] autorelease];
self.navigationItem.leftBarButtonItem = backButton;
and write the action event for backButton.
-(IBAction)gotoBack:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}
In your appdelegate in aplicationDidFinishLaunching.....
UIImage *navBG = [UIImage imageNamed:#"barra-logo-centro.png"];
[[UINavigationBar appearance] setBackgroundImage:navBG forBarMetrics:UIBarMetricsDefault];
//backbutton: 22x30 , 44x60#2x
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:[UIImage imageNamed:#"back_button.png"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackgroundImage:[UIImage imageNamed:#"normal_button.png"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
this will modify the complete proyect navigationbar and bar button ítems
Renungas answer works fine.
If you don't want to write the same code 4 times you can
always subclass UINavigationController.
I just tried this solution (with subclassing UITabBarController but nevertheless...)
and it works ok.
You can find similiar example here
Your custom code (identical to the mentioned example) should look like this:
- (void)loadView {
[super loadView];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setImage:[UIImage imageNamed:#"backbutton.png"] forState:UIControlStateNormal];
[button addTarget:self action:#selector(backAction) forControlEvents:UIControlEventTouchUpInside];
[button setFrame:CGRectMake(0, 0, 32, 32)];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
}
- (void)backAction {
[self.navigationController popViewControllerAnimated:YES];
}
As you can see all you have to do is override loadView and add a method
to perform the popVievController selector.
Good luck!