Is there a way to customize ALL uibarbuttonitems to have a background image without using UIApperance? Unfortunately our codebase has some limitations and we're not able to use UIAppearance so I'm looking for an alternative.
Thanks in advance.
Edit: I was able to do this by subclassing UIBarButtonItem and it works for most of my buttons...but I'm wondering specifically how to do it with default buttons like UITableView edit buttons and UINavigationController backbuttons.
Thanks!
You also can make subclass like this. First i go to make subclass of UIBarButtonItem. My CustomBarButtonItem.h should be like this.
#interface CustomBarButtonItem : UIBarButtonItem
+ (CustomBarButtonItem *)sharedInstance;
- (UIBarButtonItem *)backButtonwithTarget:(id)target andAction:(SEL)action;
#end
then inside my CustomBarButtonItem.m should like this.
#implementation CustomBarButtonItem
static CustomBarButtonItem * _sharedInstance = nil;
+ (CustomBarButtonItem *)sharedInstance
{
if (_sharedInstance != nil)
{
return _sharedInstance;
}
_sharedInstance = [[CustomBarButtonItem alloc] init];
return _sharedInstance;
}
- (UIBarButtonItem *)backButtonwithTarget:(id)target andAction:(SEL)action
{
UIImage *backImage = [UIImage imageNamed:#"back_btn_normal.png"];
UIImage *backPressed = [UIImage imageNamed:#"back_btn_hilight.png"];
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[backButton addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
[backButton setBackgroundImage:backImage forState:UIControlStateNormal];
[backButton setBackgroundImage:backPressed forState:UIControlStateHighlighted];
const CGFloat BarButtonOffset = 5.0f;
[backButton setFrame:CGRectMake(BarButtonOffset, 0, backImage.size.width, backImage.size.height)];
UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, backImage.size.width, backImage.size.height)];
[containerView addSubview:backButton];
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:containerView];
return item;
}
#end
Then where ever you want to make custom NavigationItem (Button in the NavigationBar) you can put the code below for me it custom.
- (void)viewDidLoad
{
self.navigationItem.leftBarButtonItem = [[CustomBarButtonItem sharedInstance] backButtonwithTarget:self andAction:#selector(searchButtonTapped:)];
}
All of my code are working fine. Hopefully it will help you.
you can do something like this. just copied it out of my project.
_leftBarButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:[self imageButton]]
style:UIBarButtonItemStyleBordered
target:self
action:#selector(showLeftViewController)];
[navController.navigationBar.topItem setLeftBarButtonItem:_leftBarButton];
To do this: create category on UINavigationBar and override method drawRect
#implementation UINavigationBar (CustomBackground)
- (void)drawRect:(CGRect)rect {
UIImage *image = [UIImage imageNamed: #"background"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
#end
This code allows you to change background for all UINavigationBar in a project.
Related
As my title, I have went through several websites and failed to achieve the result. However, I managed to add an image on the navigation bar successfully. These are two problems I'm facing currently, both are under UINavigationBar section.
1. Unable to display the rightbarbutton
2. Unable to hide the back arrow "<"
MasterViewController.m
//Back Button
UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *backBtnImage = [UIImage imageNamed:#"sidemenu.png"];
[backBtn setBackgroundImage:backBtnImage forState:UIControlStateNormal];
backBtn.frame = CGRectMake(0, 0, 54, 30);
//Rightbarbutton
UIButton *infoButton = (UIButton *) [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"icon_info.png"]];
UIBarButtonItem *infoButtonItem = [[UIBarButtonItem alloc] initWithCustomView:infoButton];
self.navigationItem.rightBarButtonItem = infoButtonItem;
//Image (Working and displaying)
UIImage *image = [UIImage imageNamed: #"icon_image.png"];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(245, 0, 30, 42)];
[imageView setImage:image];
[self.navigationController.navigationBar addSubview:imageView];
I think you should initialize you rightItem's content as an UIButton and not a cast from ImageView to UIButton... so the same procedure you are doing for the back item..
This is your UINavigationItem+AtAdditions.h file
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger, ATNavigationItemButtonSide) {
ATNavigationItemButtonSideRight,
ATNavigationItemButtonSideLeft
};
#interface UINavigationItem(ATAdditions)
// This method adds an image-only button to the requested size (right or left) of the current navigation item, the image can't be tapped
- (void)addColorfulImage:(NSString*)imageName toSide:(ATNavigationItemButtonSide)side;
#end
This is your UINavigationItem+AtAdditions.m file
#import "UINavigationItem+ATAdditions.h"
#implementation UINavigationItem(ATAdditions)
- (void)addColorfulImage:(NSString*)imageName toSide:(ATNavigationItemButtonSide)side {
// Create a button with the required image on it
UIImage *image = [UIImage imageNamed:imageName];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.bounds = CGRectMake(0, 0, image.size.width, image.size.height );
[button setImage:image forState:UIControlStateNormal];
// Stop the events (it's an image only)
button.userInteractionEnabled = NO;
// Add the button to the navigation item
if (side == ATNavigationItemButtonSideLeft) {
self.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
}
else if (side == ATNavigationItemButtonSideRight) {
self.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
}
}
#end
Later you can import the .h file and use it on the navigation item in your view controller.
I have a button search that located in the right side of the navigation.
This is my code:
UIButton *btnSearch = [UIButton buttonWithType:UIButtonTypeCustom];
btnSearch.frame = CGRectMake(0, 0, 22, 22);
[btnSearch setImage:[UIImage imageNamed:#"search_btn.png"] forState:UIControlStateNormal];
[btnSearch addTarget:self action:#selector(showSearch:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *searchItem = [[UIBarButtonItem alloc] initWithCustomView:_btnSearch];
self.navigationItem.rightBarButtonItems = searchItem ;
This is how it's look.
And I want to display search bar after clicked on the search button, and close it after clicked on cancel and then show the Navigationbar back, but I don't know how to code it.
- (IBAction)showSearch:(id)sender{
???
}
This what I want.
Please help or provide some sample code. I really need it.
Thank for reading.
Add a property UISearchBar *mySearchBar to your viewController as
#property(nonatomic, retain) UISearchBar *mySearchBar;
Conform to UISearchBarDelegate as
#interface HomeViewController () <UISearchBarDelegate>
...
...
#end
Then implement the showSearch method as
-(void)showSearch:(id)sender {
if(!mySearchBar) {
mySearchBar = [[UISearchBar alloc] init];
[mySearchBar setFrame:CGRectMake(0, 0, self.view.frame.size.width, 55)];
[mySearchBar setShowsCancelButton:YES animated:YES];
[self.view addSubview: mySearchBar];
mySearchBar.delegate = self;
self.navigationController.navigationBarHidden = YES;
}else {
searchBar.alpha = 1.0;
self.navigationController.navigationBarHidden = YES;
}
Then implement the search bar delegate method as :
- (void)searchBarCancelButtonClicked:(UISearchBar *) searchBar {
self.navigationController.navigationBarHidden = NO;
[mySearchBar setAlpha:0.0];
}
Hope you have got the idea by doing this. Also you can add it directly to navigation controller, itself, if you want & then play with Showing/ hiding the searchBar alone.
You can add it to navigation controller as just initialize the mysearchBar & add it to navigationBar as :
UIBarButtonItem *searchBarItem = [[UIBarButtonItem alloc] initWithCustomView:mySearchBar];
self.navigationItem.rightBarButtonItem = searchBarItem;
I'm experiencing excessive UIBarButtonItem padding/spacing when using the LeftBarItems and RightBarItems (see image below). The icons used on the UIBarButtonItems do not contain extra padding. So I would like to know what's causing this?
I use this in order to remove space before the first item.
However it doesn't work between system items like UIBarButtonSystemItemAdd, only with UIBarButtonItem that has an image.
#interface UIBarButtonItem (NegativeSpacer)
+(UIBarButtonItem*)negativeSpacerWithWidth:(NSInteger)width;
#end
#implementation UIBarButtonItem (NegativeSpacer)
+(UIBarButtonItem*)negativeSpacerWithWidth:(NSInteger)width {
UIBarButtonItem *item = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace
target:nil
action:nil];
item.width = (width >= 0 ? -width : width);
return item;
}
#end
Use it like this:
UIBarButtonItem *item0 = [UIBarButtonItem negativeSpacerWithWidth:13];
UIBarButtonItem *item1 = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"sidebar.png"]
style:UIBarButtonItemStylePlain
target:vc
action:#selector(sideMenuAction:)];
NSArray* items = #[item0, item1];
[vc.navigationItem setLeftBarButtonItems:items animated:NO];
[vc.navigationItem setLeftItemsSupplementBackButton:YES];
You can move the image
self.myBarButtonItem.imageInsets = UIEdgeInsetsMake(0, 25, 0, -25);
Apple silently increased the horizontal spacing constraints for UIBarButtonItems and sadly, still hasn't added any UIAppearance methods to adjust the horizontal positioning of UIBarButtonItems.
The best solution (which worked for me) is to wrap your UIBarButtonItems in a UIView using initWithCustomView: and adjust the bounds of that custom view to get your desired positioning. Here's a good answer on how to do this.
If you want to take things a step further, you can create a category on UIBarButtonItem with class methods that return the bar buttons you use throughout your app. That way, when you a need a bar button, you can call something like:
self.navigationItem.leftBarButtonItem = [UIBarButtonItem mySearchBarButtonItemWithTarget:self selector:#selector(search)];
There are two kinds of button on the navigation bar in iOS 7: button with image and button with text. I wrote a class to do it. Here is how:
GlobalUICommon.h:
#interface UIBarButtonItem(CustomUIOfONE)
+ (UIBarButtonItem*)barItemWithImage:(UIImage*)image highlightedImage:(UIImage*)highlightedImage xOffset:(NSInteger)xOffset target:(id)target action:(SEL)action;
+ (UIBarButtonItem*)barItemWithTitle:(NSString*)title xOffset:(NSInteger)xOffset target:(id)target action:(SEL)action;
#end
GlobalUICommon.m:
#implementation UIBarButtonItem(CustomUIOfONE)
+ (UIBarButtonItem*)barItemWithImage:(UIImage*)image highlightedImage:(UIImage*)highlightedImage xOffset:(NSInteger)xOffset target:(id)target action:(SEL)action
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setFrame:CGRectMake(0, 0, image.size.width, image.size.height)];
[button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
[button setImage:image forState:UIControlStateNormal];
[button setImage:highlightedImage forState:UIControlStateHighlighted];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
[button setImageEdgeInsets:UIEdgeInsetsMake(0, xOffset, 0, -xOffset)];
}
UIBarButtonItem *customUIBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
return customUIBarButtonItem;
}
+ (UIBarButtonItem*)barItemWithTitle:(NSString*)title xOffset:(NSInteger)xOffset target:(id)target action:(SEL)action
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setTitle:title forState:UIControlStateNormal];
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[button setTitleColor:[UIColor redColor] forState:UIControlStateHighlighted];
[button.titleLabel setFont:[UIFont systemFontOfSize:15]];
[button setFrame:CGRectMake(0, 0, [button.titleLabel.text sizeWithFont:button.titleLabel.font].width + 3, 24)];
[button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
[button setContentEdgeInsets:UIEdgeInsetsMake(0, xOffset, 0, -xOffset)];
}
UIBarButtonItem *customUIBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
return customUIBarButtonItem;
}
#end
YourViewController.m:
Example for Button with Image:
UIBarButtonItem* leftButtomItem = [UIBarButtonItem barItemWithImage:[UIImage imageNamed:#"yourImage"]
highlightedImage:[UIImage imageNamed:#"yourImage"]
xOffset:-11
target:self
action:#selector(yourHandler)];
self.navigationItem.leftBarButtonItem = leftButtomItem;
UIBarButtonItem* rightButtonItem = [UIBarButtonItem barItemWithImage:[UIImage imageNamed:#"yourImage"]
highlightedImage:[UIImage imageNamed:#"yourImage"]
xOffset:11
target:self
action:#selector(yourHandler)];
self.navigationItem.rightBarButtonItem = rightButtonItem;
Example for Button with Text:
self.navigationItem.leftBarButtonItem = [UIBarButtonItem barItemWithTitle:#"yourText" xOffset:-11 target:self action:#selector(yourHandler:)];
self.navigationItem.rightBarButtonItem = [UIBarButtonItem barItemWithTitle:#"yourText" xOffset:11 target:self action:#selector(yourHandler:)];
That's it.
I solved this by using storybord interface.
1.Select the Bar item.
2.Select the Size Inspector.
Here you can find image Inset,using top,bottom AND left , right you can change the position of Bar Item.
As #Luda comments, the solution is to
self.myBarButtonItem.imageInsets = UIEdgeInsetsMake(0, 25, 0, -25);
However, #andrrs also points a problem here when inset is large: the hit area. In this case,we have to implement a way to setHitTestEdgeInsets. Below is an category method:
#implementation UIButton (Extensions)
#dynamic hitTestEdgeInsets;
static const NSString *KEY_HIT_TEST_EDGE_INSETS = #"HitTestEdgeInsets";
-(void)setHitTestEdgeInsets:(UIEdgeInsets)hitTestEdgeInsets {
NSValue *value = [NSValue value:&hitTestEdgeInsets withObjCType:#encode(UIEdgeInsets)];
objc_setAssociatedObject(self, &KEY_HIT_TEST_EDGE_INSETS, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(UIEdgeInsets)hitTestEdgeInsets {
NSValue *value = objc_getAssociatedObject(self, &KEY_HIT_TEST_EDGE_INSETS);
if(value) {
UIEdgeInsets edgeInsets; [value getValue:&edgeInsets]; return edgeInsets;
}else {
return UIEdgeInsetsZero;
}
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
if(UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero) || !self.enabled || self.hidden) {
return [super pointInside:point withEvent:event];
}
CGRect relativeFrame = self.bounds;
CGRect hitFrame = UIEdgeInsetsInsetRect(relativeFrame, self.hitTestEdgeInsets);
return CGRectContainsPoint(hitFrame, point);
}
#end
I want to set background image for every navigation item(button) (universal).
How should these images look?
Are there fixed dimensions or it is possible to create repeating background image?
How to programmatically change then this background image?
Thank's for help
UPDATE:
I found this code for changing background of this button but it's no working.
UIImage *barButton = [UIImage imageNamed:#"button_background.png"];
[[UIBarButtonItem appearance] setBackgroundImage:barButton forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
And I don't know which dimensions should be.
P.S. I want to change background of button just like you can change background color (self.navigation.controller.nvigationBar.tintColor..)
The code below might solve your problem
UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *backBtnImage = [UIImage imageNamed:#"button_background.png"] ;
[backBtn setBackgroundImage:backBtnImage forState:UIControlStateNormal];
[backBtn addTarget:self action:#selector(goback) forControlEvents:UIControlEventTouchUpInside];
backBtn.frame = CGRectMake(0, 0, 54, 30);
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithCustomView:backBtn] ;
self.navigationItem.leftBarButtonItem = cancelButton;
u need to create Custom Back Button i m afraid
check this code out
UIBarButtonItem *backButton = [[UIBarButtonItem alloc]
initWithImage:[UIImage imageNamed:#"yourImage.png"]
style:UIBarButtonItemStyleBordered
target:nil
action:nil];
self.navigationItem.backBarButtonItem = backButton;
[backButton release];
Let me know if it worked
Cheers
what about categories?
For example, you can do so:
#interface UIBarButtonItem(JFAdditions)
- (id)initWithTitle:(NSString *)title image:(UIImage*)img;//add target and action if need
#end
#implementation UIBarButtonItem(JFAdditions)
- (id)initWithTitle:(NSString *)title image:(UIImage*)img
{
UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];
//add background image and text on button, target and action if need, also
self = [self initWithCustomView:btn];
return self;
}
I have a UINavigationBar with a custom background and my own back button. I did the following to try achieve this.
//custom background
UIImageView *background = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"navigation-bar.png"]] autorelease];
[self.navigationController.navigationBar insertSubview:background atIndex:0];
//custom back button
UIImage *buttonImage = [UIImage imageNamed:#"btn_back.png"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setImage:buttonImage forState:UIControlStateNormal];
button.frame = CGRectMake(0, 0, buttonImage.size.width, buttonImage.size.height);
[button addTarget:self action:#selector(backPressed:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *customBarItem = [[UIBarButtonItem alloc] initWithCustomView:button];
self.navigationItem.leftBarButtonItem = customBarItem;
[customBarItem release];
The back button always works, but it can only be seen if I don't apply my custom background. Could someone please point out how to achieve both?
Thanks in advance.
Ricky.
You can set background image of navigation bar by using this
#implementation UINavigationBar (UINavigationBarCategory)
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
if([self isMemberOfClass:[UINavigationBar class]])
{
UIImage *image;
image=[UIImage imageNamed:#"nav_bar_img"];
CGContextClip(ctx);
CGContextTranslateCTM(ctx, 0, image.size.height);
CGContextScaleCTM(ctx, 1.0, -1.0);
CGContextDrawImage(ctx,
CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), image.CGImage);
}
else
{
[super drawLayer:layer inContext:ctx];
}
}
#end
I ended up doing the following.
#implementation UINavigationBar (Category)
- (void)drawRect:(CGRect)rect
{
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate.navImage drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
#end
Every time I want to change the background of the bar, I change the delegate's navImage property and call -setNeedsDisplay on my navigation bar.
Thanks for your help.