UIBarButtonItem with custom image and no border - ios

I want to create a UIBarButtonItem with a custom image, but I don't want the border that iPhone adds, as my Image has a special border.
It's the same as the back button but a forward button.
This App is for an inHouse project, so I don't care if Apple reject or approves it or likes it :-)
If I use the initWithCustomView:v property of the UIBarButtonItem, I can do it:
UIImage *image = [UIImage imageNamed:#"right.png"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setBackgroundImage: [image stretchableImageWithLeftCapWidth:7.0 topCapHeight:0.0] forState:UIControlStateNormal];
[button setBackgroundImage: [[UIImage imageNamed: #"right_clicked.png"] stretchableImageWithLeftCapWidth:7.0 topCapHeight:0.0] forState:UIControlStateHighlighted];
button.frame= CGRectMake(0.0, 0.0, image.size.width, image.size.height);
[button addTarget:self action:#selector(AcceptData) forControlEvents:UIControlEventTouchUpInside];
UIView *v=[[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, image.size.width, image.size.height) ];
[v addSubview:button];
UIBarButtonItem *forward = [[UIBarButtonItem alloc] initWithCustomView:v];
self.navigationItem.rightBarButtonItem= forward;
[v release];
[image release];
This works, but if I have to repeat this process in 10 views, this is not DRY.
I suppose I have to subclass, but what ?
NSView ?
UIBarButtonItem ?
thanks,
regards,

Another simple solution is
Drag a standard UIButton
Set the button's style to custom and set your image for that button
Drag it onto the UINavigationBar
Set Selector

You can add a method to UIBarButtonItem without subclassing it using custom category:
#interface UIBarButtonItem(MyCategory)
+ (UIBarButtonItem*)barItemWithImage:(UIImage*)image target:(id)target action:(SEL)action;
#end
#implementation UIBarButtonItem(MyCategory)
+ (UIBarButtonItem*)barItemWithImage:(UIImage*)image target:(id)target action:(SEL)action{
// Move your item creation code here
}
#end
So anywhere in your code you can create bar item calling this method (provided that you include a header with its declaration).
P.S. You do not need to use 'v' UIView as you can create UIBarButtonItem with a button as custom view directly.
P.P.S. You also need [forward release] in your code.

I found it this ways easy. It is sugested on top. "random.png" has to be in project. Just drag and drop any image.
UIButton *a1 = [UIButton buttonWithType:UIButtonTypeCustom];
[a1 setFrame:CGRectMake(0.0f, 0.0f, 25.0f, 25.0f)];
[a1 addTarget:self action:#selector(randomMsg) forControlEvents:UIControlEventTouchUpInside];
[a1 setImage:[UIImage imageNamed:#"config.png"] forState:UIControlStateNormal];
UIBarButtonItem *random = [[UIBarButtonItem alloc] initWithCustomView:a1];
//? line incomplete ?// imageNamed:#"random.png"] style:UIBarButtonItemStylePlain target:self action:#selector(randomMsg)];
self.navigationItem.rightBarButtonItem = random;

An alternative is to subclass UIBarButtonItem. Why? So that the action is invoked on the target with the correct sender. In the code above, the sender argument in the action message is the UIButton instance, not the UIBarButtonItem instance. This would be important, for example, if you wish to present a UIPopoverController from the bar button item. By subclassing UIBarButtonItem, you can add an ivar that retains the original target, allowing our subclass instances to intercept, modify, and forward the action message with the proper sender.
So, CCFBarButtonItem.h:
#import <uIKit/UIBarButtonItem.h>
#interface CCFBarButtonItem : UIBarButtonItem
{
#protected
id _originalTarget;
}
- (id)initWithImage:(UIImage *)image target:(id)target action:(SEL)action;
#end
and CCFBarButtonItem.m
#import "CCFBarButtonItem.h"
#import <UIKit/UIButton.h>
#import <UIKit/UIView.h>
#import <UIKit/UIImage.h>
#implementation CCFBarButtonItem
#pragma mark - Object life cycle
- (id)initWithImage:(UIImage *)image target:(id)target action:(SEL)action;
{
_ASSIGN( _originalTarget, target );
UIButton *imgButton = [UIButton buttonWithType:UIButtonTypeCustom];
[imgButton setImage:image forState:UIControlStateNormal];
imgButton.frame = CGRectMake(0.0, 0.0, image.size.width, image.size.height);
[imgButton addTarget:self action:action forControlEvents:UIControlEventTouchUpInside];
self = [super initWithCustomView:imgButton];
return self;
}
- (void)dealloc;
{
MCRelease(_originalTarget);
[super dealloc];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
{
if( [_originalTarget respondsToSelector:aSelector] )
{
return [_originalTarget methodSignatureForSelector:aSelector];
}
else
{
return [super methodSignatureForSelector:aSelector];
}
}
- (void)forwardInvocation:(NSInvocation *)anInvocation;
{
SEL aSelector = [anInvocation selector];
if( [_originalTarget respondsToSelector:aSelector] )
{
// modify the 'sender' argument so that it points to self
[anInvocation setArgument:&self atIndex:2];
[anInvocation invokeWithTarget:_originalTarget];
}
else
{
[self doesNotRecognizeSelector:aSelector];
}
}
#end

UIBarButtonItem *menuItem = [[UIBarButtonItem alloc] initWithImage: [UIImage imageNamed:#"icon-menu.png"]
style:UIBarButtonItemStylePlain
target:self
action:#selector(showMenu)];

This can also be done programmatically as well (of-course):
First, create a custom view. This custom view can contain an image, button or whatever else you would like. The custom view can be made programmatically or in IB:
UIImage *customImage = [UIImage imageNamed:#"imageName"];
UIView *customView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, customImage.size.width, customImage.size.height)];
customView.backgroundColor = [UIColor colorWithPatternImage:customImage];
Next, create a UIBarButtonItem and initialize it with the custom view.
UIBarButtonItem *customBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:customView];
Now, just add the custom UIBarButton to the leftBarButtonItem:
self.navigationItem.leftBarButtonItem = customBarButtonItem;

Ok that category works very good because there are no problems with Popovercontroller :-)
#import <UIKit/UIKit.h>
#interface UIBarButtonItem (BarButtonItemExtended)
+ (UIBarButtonItem*)barItemWithImage:(UIImage*)image target:(id)target action:(SEL)action;
-(void)performBarButtonAction:(id)sender;
#end
#import "UIBarButtonItem+BarButtonItemExtended.h"
#implementation UIBarButtonItem (BarButtonItemExtended)
+ (UIBarButtonItem*)barItemWithImage:(UIImage*)image target:(id)target action:(SEL)action
{
UIButton *imgButton = [UIButton buttonWithType:UIButtonTypeCustom];
[imgButton setImage:image forState:UIControlStateNormal];
imgButton.frame = CGRectMake(0.0, 0.0, image.size.width, image.size.height);
UIBarButtonItem *b = [[UIBarButtonItem alloc]initWithCustomView:imgButton];
[imgButton addTarget:b action:#selector(performBarButtonAction:) forControlEvents:UIControlEventTouchUpInside];
[b setAction:action];
[b setTarget:target];
return b;
}
-(void)performBarButtonAction:(UIButton*)sender
{
[[self target] performSelector:self.action withObject:self];
}
#end

One another solution, think it's simpler in case when creating button programatically:
UIBarButtonItem *button = [[UIBarButtonItem alloc] initWithImage:defaultImage
landscapeImagePhone:landscapeImage
style:UIBarButtonItemStylePlain
target:self
action:#selector(someSelector)];
[button setBackgroundImage:[UIImage new] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[button setBackgroundImage:[UIImage new] forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];

Check this out simple solution.
- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController
{
barButtonItem.image = [UIImage imageNamed:#"navButton.png"];
barButtonItem.style = UIBarButtonItemStylePlain;
[barButtonItem setBackgroundImage:[UIImage imageNamed:#"1x1.png"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES];
self.masterPopoverController = popoverController;
}
Here 1x1.png is a 1 pixel transparent png image which you can download from the link below
http://commons.wikimedia.org/wiki/File:1x1.png

Related

iOS 11 - how to change the position of rightBarButtonItems?

the code in iOS 10 or earlier is worked
UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
[btn1 setTitle:#"yyyyy" forState:UIControlStateNormal];
[btn1 setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[btn1 sizeToFit];
UIBarButtonItem *item1 = [[UIBarButtonItem alloc] initWithCustomView:btn1];
UIBarButtonItem *fixed = [[UIBarButtonItem alloc] initWithBarButtonSystemItem: UIBarButtonSystemItemFixedSpace target:nil action:nil];
fixed.width = -22;
self.navigationItem.rightBarButtonItems = #[fixed, item1];
if i want to do the same thing in iOS 11, what can i do fo it?
We have acomplished this by subclassing UINavigationBar:
#import <UIKit/UIKit.h>
#interface InsetButtonsNavigationBar : UINavigationBar
#end
and setting layoutMargins
#import "InsetButtonsNavigationBar.h"
#implementation InsetButtonsNavigationBar
- (void)layoutSubviews {
[super layoutSubviews];
for (UIView * view in self.subviews) {
view.layoutMargins = UIEdgeInsetsMake(0, 8, 0, 8);
}
}
#end

Change the background color of a UIBarButtonItem in ios 7

I have a UINavigationBar with a right UIBarButtonItem.
How do I change just the background color of the button (not the text)?
UIBarButtonItem *postButton = [[UIBarButtonItem alloc] initWithTitle:#"POST" style:UIBarButtonItemStylePlain target:self action:#selector(postButtonPressed:)];
// Changes text color
postButton.tintColor = [UIColor blueColor];
How do I make the background color change?
Using storyboard or XIB, You could add UIButton directly to a navigation bar and change UIButton background color.
Use appearance proxy for this,
Create a 1x1 pixel image with the color you prefer. In this case this image's name is "icons_gb.png". Then add following code to your AppDelegate.m . Image color will be repeated in the button's background.
UIImage *btnBg = [[UIImage imageNamed:#"icons_bg"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
[[UIBarButtonItem appearance] setBackgroundImage:btnBg
forState:UIControlStateNormal
barMetrics:UIBarMetricsDefault];
you can change it by:
yourNavigationBar.barTintColor = [UIColor yellowColor];
The color of navigation bar also be changed.
If you want to change only UIBarButtonItem, use my custom class:
UIBarButtonItem+Custom.h
#import <UIKit/UIKit.h>
#interface UIBarButtonItem (Custom)
+ (UIBarButtonItem *) barItemWithImage:(UIImage *)img size:(CGSize)size target:(id)target action:(SEL)selector;
#end
UIBarButtonItem+Custom.m
#import "UIBarButtonItem+Custom.h"
#implementation UIBarButtonItem (Custom)
+ (UIBarButtonItem *) barItemWithImage:(UIImage *)img size:(CGSize)size target:(id)target action:(SEL)selector
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setImage:img forState:UIControlStateNormal];
button.frame= CGRectMake(0.0, 0.0, size.width, size.height);
[button addTarget:target action:selector forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *forward = [[[UIBarButtonItem alloc] initWithCustomView:button] autorelease];
return forward;
}
#end

UINavigationBar - Change Height / Add Big Button

I need to change the height of my Navigation Bar and add a custom Image button to the top left corner. I am part way there, but lost now on getting the custom Image button in the right position. Here is what I have:
To adjust the height I have created a UINavBar category with one method as follows:
#implementation UINavigationBar (myNavBar)
- (CGSize)sizeThatFits:(CGSize)size {
CGSize newSize = CGSizeMake(768,80);
return newSize;
}
#end
I have also created a UINavigationController subclass to modify the button. Here is the viewDidLoad from that class:
UIImage *navBackgroundImage = [UIImage imageNamed:#"bar"];
[[UINavigationBar appearance] setBackgroundImage:navBackgroundImage forBarMetrics:UIBarMetricsDefault];
// Change the appearance of back button
UIImage *backButtonImage = [[UIImage imageNamed:#"back_off"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 13, 0, 6)];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:backButtonImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
// Change the appearance of other navigation button
UIImage *barButtonImage = [[UIImage imageNamed:#"menu_off"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 6, 0, 6)];
[[UIBarButtonItem appearance] setBackgroundImage:barButtonImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
So far this solution resizes the top nav bar, but positions my button in a weird position. Here are what I want vs. what is happening:
What I want
What I get
I have a UIBarButtonItem Category that I use which has an offset property:
UIBarButtonItem+CustomImage.h
#interface UIBarButtonItem (CustomImage)
+ (UIBarButtonItem*)barItemWithImage:(UIImage*)image target:(id)target action:(SEL)action offset:(CGPoint)offset;
#end
UIBarButtonItem+CustomImage.m
#import "UIBarButtonItem+CustomImage.h"
#implementation UIBarButtonItem (CustomImage)
+ (UIBarButtonItem *)barItemWithImage:(UIImage *)image target:(id)target action:(SEL)action offset:(CGPoint)offset {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setImage:image forState:UIControlStateNormal];
[button setFrame:CGRectMake(0.0, 0.0, image.size.width, image.size.height)];
[button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
[button setBounds:CGRectOffset(button.bounds, 0.0, -10.0)];
UIView *container = [[UIView alloc] initWithFrame:button.frame];
[container setBounds:CGRectOffset(container.bounds, offset.x, offset.y)];
[container addSubview:button];
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:container];
return item;
}
#end
Example Usage
#import "UIBarButtonItem+CustomImage.h"
UIBarButtonItem *settingsButton = [UIBarButtonItem barItemWithImage:settingsImage
target:self
action:#selector(revealSettings:)
offset:CGPointMake(0.0, 0.0)];
[self.navigationItem setLeftBarButtonItem:settingsButton];

iOS 7 custom back button

I want to use custom back button. in iOS 6 everything is perfect but iOS 7 is strange.
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:[[UIImage imageNamed:#"back_button_normal"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12.0, 0, 12.0)] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
first, it has no iOS 7 arrow and no background image.
(Russian locale)
then, if you press the button background image appears. Also i had background image set for UIControlStateHighlighted state and when you hold the button pressed highlighted image appears too. After any back button once pressed all back buttons have background image.
BUT! If you present modal view controller, dismiss it, then push any view controller - iOS 7 arrow will appear at every back button.
I use DP5. Is that a UIKit bug?
PS Also i tried to create back button manually, using UIBarButtonItem, set background image to it, then self.navigationItem.backBarButtonItem = barButtonItem; Did not help.
Then i tried to set background image to disabled state and change enabled property of my bar button item, did not help too.
This is not a bug, this how Back button looks in iOS 7. For example:
You should probably use the new concept for your application, and not to set background image for back button in iOS 7.
If you still want you back button have the same as it looked in iOS6 than you should probably create those back buttons manually:
- (void)loadView
{
[super loadView];
UIButton *backButton = [[UIButton alloc] initWithFrame: CGRectMake(0, 0, 60.0f, 30.0f)];
UIImage *backImage = [[UIImage imageNamed:#"back_button_normal.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12.0f, 0, 12.0f)];
[backButton setBackgroundImage:backImage forState:UIControlStateNormal];
[backButton setTitle:#"Back" forState:UIControlStateNormal];
[backButton addTarget:self action:#selector(popBack) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
self.navigationItem.leftBarButtonItem = backButtonItem;
}
-(void) popBack {
[self.navigationController popViewControllerAnimated:YES];
}
Edit: Not to break Swipe Gesture (Here is a source)
self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;
The custom background image not appearing on the first push was fixed in iOS 7 GM.
To hide standard back indicator use this code:
if ([UINavigationBar instancesRespondToSelector:#selector(setBackIndicatorImage:)]) { // iOS 7
[navigationBarAppearance setBackIndicatorImage:[UIImage imageNamed:#"transparent_1px"]];
[navigationBarAppearance setBackIndicatorTransitionMaskImage:[UIImage imageNamed:#"transparent_1px"]];
}
The custom background image not appearing initially was not fixed in iOS7 GM or final, as far as I can tell. I see the same problem. It does seem to be an Apple bug; the private view Apple uses simply does not get a setNeedsDisplay call when it needs it on initial display. Doing anything to it which causes that call should fix it -- like pressing on it (which likely changes internal state so it calls setNeedsDisplay on itself), or bringing a modal up (which probably forces a redisplay of the entire view hierarchy on the next viewWillAppear: call).
Using leftBarItems instead also can work, but that may cause a lot of maintenance issues with existing code (some screens may have their own left items, expecting that when set back to nil they restore the original back item, for example).
As mentioned, ideally you would be able to change to a borderless look on iOS7, which means that the bug isn't really apparent (since there is no background image). For some iOS6/iOS7 transition situations though, that may be difficult (lots of screens, and/or the need to support older iOS versions for a while and too hard to have two looks implemented, and it doesn't look good borderless without other changes). If that's the case, the following patch should work:
#import <objc/runtime.h>
#implementation UINavigationBar (BackButtonDisplayFix)
+ (void)load
{
if ([UIDevice currentDevice].systemVersion.intValue >= 7)
{
/*
* We first try to simply add an override version of didAddSubview: to the class. If it
* fails, that means that the class already has its own override implementation of the method
* (which we are expecting in this case), so use a method-swap version instead.
*/
Method didAddMethod = class_getInstanceMethod(self, #selector(_displaybugfixsuper_didAddSubview:));
if (!class_addMethod(self, #selector(didAddSubview:),
method_getImplementation(didAddMethod),
method_getTypeEncoding(didAddMethod)))
{
Method existMethod = class_getInstanceMethod(self, #selector(didAddSubview:));
Method replacement = class_getInstanceMethod(self, #selector(_displaybugfix_didAddSubview:));
method_exchangeImplementations(existMethod, replacement);
}
}
}
- (void)_displaybugfixsuper_didAddSubview:(UIView *)subview
{
[super didAddSubview:subview];
[subview setNeedsDisplay];
}
- (void)_displaybugfix_didAddSubview:(UIView *)subview
{
[self _displaybugfix_didAddSubview:subview]; // calls the existing method
[subview setNeedsDisplay];
}
#end
Note: UINavigationBar does currently have an override of the method in question, so I'd expect the method_exchangeImplementations style to be used. I just added the other stuff for safety in case Apple changes their code. We may go borderless ourselves, but I did find this approach worked as an option (until a more thorough UI uplift), at least.
Additional note: This bug appears to be fixed in iOS 7.1. So, the patch could be conditionalized to only install the methods if running >= 7.0 and < 7.1.
There is a better solution that doesn't involve method swizzling.
You need to add UINavigationViewControllerDelegate method somewhere in your app.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
dispatch_async(dispatch_get_main_queue(), ^{
[[navigationController.navigationBar subviews] makeObjectsPerformSelector:#selector(setNeedsDisplay)];
});
}
My solution is for iOS 7 and above.
At first, make default back button invisible.
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"" style:UIBarButtonItemStylePlain target:nil action:nil];
then, set default backIndicatorImage of back button using custom image.
[UINavigationBar appearance].backIndicatorImage = [[UIImage imageNamed:#"topbar_icon_back_n.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
[UINavigationBar appearance].backIndicatorTransitionMaskImage = [[UIImage imageNamed:#"topbar_icon_back_p.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
At this point, make custom UINavigationBar for resizing _UINavigationBarBackIndicatorView which contains above backIndicatorImage.
const CGPoint SANavigationBarOffset = {-8, 11.5};
#implementation SANavigationBar
- (void)layoutSubviews
{
[super layoutSubviews];
// set back button position
NSArray *classNamesToReposition = #[#"_UINavigationBarBackIndicatorView"];
for (UIView *view in [self subviews]) {
if ([classNamesToReposition containsObject:NSStringFromClass([view class])]) {
CGRect frame = [view frame];
frame.origin.x = 0;
frame.origin.y = 0;
[view setFrame:frame];
}
}
}
#end
then, set it as my navigationBar
// set custom NavagationBar for back button position
[self.navigationController setValue:[[SANavigationBar alloc] init] forKey:#"navigationBar"];
Add button as navigation item in ios7 as below
UIButton *btnAdd = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 60, 30)];
[btnAdd setContentMode:UIViewContentModeScaleAspectFit];
[btnAdd setBackgroundImage:[UIImage imageNamed:#"back.png"] forState:UIControlStateNormal];
[btnAdd addTarget:self action:#selector(backButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *btnAdd = [[UIBarButtonItem alloc] initWithCustomView:imView];
self.navigationItem.rightBarButtonItem = btnAdd;
Using Swift you can just add a extension:
extension UIViewController: UIGestureRecognizerDelegate {
func popBack() {
self.navigationController?.popViewControllerAnimated(true)
}
func enableCustomBackButtom() {
self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(named: "icon-back"), style: UIBarButtonItemStyle.Plain, target: self, action:"popBack")
self.navigationController?.interactivePopGestureRecognizer.delegate = self
}
}
And in your UIViewController use like this:
self.enableCustomBackButtom()
I just did it providing the same behaviour as in iOS6 (notice that navigationBar is the UINavigationBar), make sure that navigationBar has a topItem
UINavigationItem *topItemNavigation = [navigationBar topItem];
UIBarButtonItem *barButtonTopItemNavigation = [[UIBarButtonItem alloc] initWithTitle:topItemNavigation.title style:UIBarButtonItemStyleBordered target:nil action:nil];
[barButtonTopItemNavigation setBackButtonBackgroundImage:YOUR_IMAGE_BACKGROUND forState:UIControlStateNormal barMetrics:UIBarMetricsDefault ];
[topItemNavigation setBackBarButtonItem: barButtonTopItemNavigation];
}
My solution was to write a category on UINavigationItem. This is for iOS7.
- (void)mdSetCustomBackButton:(UINavigationController *)navigationController
{
MDBackButton *backButton = [[MDBackButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 44.0, 44.0) navigationController:navigationController];
[backButton addTarget:self action:#selector(popBack:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
[self setLeftBarButtonItem:barButtonItem];
[navigationController.interactivePopGestureRecognizer setDelegate:(id<UIGestureRecognizerDelegate>)self];
}
- (void)popBack:(MDBackButton *)sender
{
[sender.navigationController popViewControllerAnimated:YES];
}
And subclass UIButton to add a UINavigationController property (to pop and set swipe back delegate).
#property (nonatomic, weak) UINavigationController *navigationController;
#implementation MDBackButton
- (id)initWithFrame:(CGRect)frame navigationController:(UINavigationController *)navigationController
{
self = [super initWithFrame:frame];
if(self){
_navigationController = navigationController;
[self setImage:[UIImage imageNamed:#"back_button"] forState:UIControlStateNormal];
}
return self;
}
This is work for me:
- (void)setCustomNavigationBackButton
{
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"" style:UIBarButtonItemStylePlain target:nil action:nil];
UIImage *myIcon = [self imageWithImage:[UIImage imageNamed:#"backbutton.png"] scaledToSize:CGSizeMake(20, 20)];
self.navigationController.navigationBar.backIndicatorImage = myIcon;
self.navigationController.navigationBar.backIndicatorTransitionMaskImage = myIcon;
}
- (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize
{
//UIGraphicsBeginImageContext(newSize);
// In next line, pass 0.0 to use the current device's pixel scaling factor (and thus account for Retina resolution).
// Pass 1.0 to force exact pixel size.
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Also, custom font with custom color:
//self.navigationController.navigationBar.tintColor = [UIColor whiteColor];
[[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil] setTitleTextAttributes:
#{NSForegroundColorAttributeName:[UIColor whiteColor],
NSFontAttributeName:[UIFont fontWithName:#"Signika-Bold" size:20]}
forState:UIControlStateNormal];
Reference: https://stackoverflow.com/a/2658801/1371949
I use these codes below, which works in iOS 8
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.translatesAutoresizingMaskIntoConstraints = NO;
button.exclusiveTouch = YES;
button.titleLabel.font = [UIFont systemFontOfSize:14.0];
[button setTitleColor:kWhiteColor forState:UIControlStateNormal];
[button setTitleColor:[UIColor colorWithRed:1/255.0 green:36/255.0 blue:60/255.0 alpha:1.0] forState:UIControlStateHighlighted];
[button setTitle:#"Back" forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:#"barbutton_back"] forState:UIControlStateNormal];
[button setImageEdgeInsets:UIEdgeInsetsMake(1.0, 0.0, 0.0, 0.0)];
CGSize fontSize = [button.titleLabel sizeThatFits:CGSizeMake(100.0, 30.0)];
button.frame = CGRectMake(0.0, 0.0, button.imageView.image.size.width+fontSize.width, 30.0);
UIBarButtonItem *barbtn = [[UIBarButtonItem alloc] initWithCustomView:button];
//fix iOS 7 left margin
UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
negativeSpacer.width = -10;
self.navigationItem.leftBarButtonItems = [NSArray arrayWithObjects:negativeSpacer,barbtn, nil];
-(void) viewWillAppear:(BOOL)animated
{
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setFrame:CGRectMake(0, 0, 30, 44)];
[btn setImage:[UIImage imageNamed:#"btnBack.png"] forState:UIControlStateNormal];
[btn addTarget:self action:#selector(PopToView) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *btnBack = [[UIBarButtonItem alloc] initWithCustomView:btn];
[btnBack setTintColor:[UIColor whiteColor]];
[[self.navigationController navigationItem] setLeftBarButtonItem:btnBack];
}

Updating UIImage on UiBarButtonItem from identifier to image

I am currently making an application for IOS but I am having trouble changing an image... what a shame...
The situation is like this :
I have a toolbar containing my items, and one of this items is the play button created from the identifier "Play". So I have my Play button without problem. Now I just want to change it to a pause image when I click on item and then switch again etc.
So I liked in my .h the item giving me :
#property (weak, nonatomic) IBOutlet UIBarButtonItem *play;
I tried many answers I found on this website and none of them did work on my case :/
The last one I tried was something like this :
UIButton *button1=[UIButton buttonWithType:UIButtonTypeCustom];
[button1 setFrame:CGRectMake(10.0, 2.0, 45.0, 40.0)];
[button1 addTarget:self action:#selector(showLeft:) forControlEvents:UIControlEventTouchUpInside];
[button1 setImage:[UIImage imageNamed:#"pause.png"] forState:UIControlStateNormal];
UIBarButtonItem *button = [[UIBarButtonItem alloc]initWithCustomView:button1];
self.play = button;
I also tried :
self.play.customView = button1;
But none of them really work, I can get the image by doing
self.view = button1;
But that only the picture (so the creation of the UIImage is okay) in the middle of the screen so....
(If you can also tell me how to go back to the play using identifier it would also be very helpful thanks a lot)
Thanks for your help.
If the button is already created and you have an outlet for it, you can simply set it like this:
UIImage* backgroundImage = [UIImage yourImage];
[self.barButtonItem setImage:backgroundImage];
Works like charm.
I think this should work.
-(IBAction)buttonClick:(UIBarButtonItem *)sender {
if ([[sender backgroundImageForState:UIControlStateNormal barMetrics:UIBarMetricsDefault] isEqual:[UIImage imageNamed:#"Play.jpg"]]) {
[sender setBackgroundImage:[UIImage imageNamed:#"Pause.jpg"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
}else{
[sender setBackgroundImage:[UIImage imageNamed:#"Play.jpg"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
}
}
Of course, you'll have to set the background image initially (in viewDidLoad) to the "Play" image for this to work.
After Edit:
If you want to use the system play and pause button, as far as I know, you have to replace the button. I don't think there's a way to just change the image. So, I did it this way. The button was set up in IB with the outlet playPauseButton, and the action playClick. I also made an outlet to the tool bar (toolBar).
-(IBAction)playClick:(UIBarButtonItem *)sender {
UIBarButtonItem *pause = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPause target:self action:#selector(pauseClick:)];
NSMutableArray *tbItems = [self.toolBar.items mutableCopy];
[tbItems removeObject:self.playPauseButton];
self.playPauseButton = pause;
[tbItems addObject:pause];
self.toolBar.items = tbItems;
}
-(void)pauseClick:(UIBarButtonItem *)sender {
UIBarButtonItem *play = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPlay target:self action:#selector(playClick:)];
NSMutableArray *tbItems = [self.toolBar.items mutableCopy];
[tbItems removeObject:self.playPauseButton];
self.playPauseButton = play;
[tbItems addObject:play];
self.toolBar.items = tbItems;
}
Create a custom button. Change the size of the button to match the image. Set image of the button. Add button view to UIBarButton play.
.h
IBOutlet UIBarButtonItem *play;
#property(nonatomic, retain) IBOutlet UIBarButtonItem *play;
.m
UIImage *image = [UIImage imageNamed:#"image.png"];
UIButton *button1 = [UIButton buttonWithType:UIButtonTypeCustom];
button1.bounds = CGRectMake( 0, 0, image.size.width, image.size.height );
[button1 setImage:image forState:UIControlStateNormal];
play = [[UIBarButtonItem alloc] initWithCustomView:button1];
Make sure you connect your outlet for the UIBarButtonItem in Interface Builder.
Good luck!
The answer of rdelmar it's great. I would like to add an enhancement that avoid a flickering of the button because the remove and add of the button.
-(void)pauseClick:(UIBarButtonItem *)sender {
UIBarButtonItem *play = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPlay target:self action:#selector(playClick:)];
NSMutableArray *tbItems = [self.toolBar.items mutableCopy];
self.playPauseButton = play;
//instead of remove and add the new button, use the replaceObjectAtIndex method.
[tbItems replaceObjectAtIndex:2 withObject:play];
self.toolBar.items = tbItems;
}
You can try something like this for your custom button as one of the UIBarButtonItem.
- (void)viewDidLoad
{
// add our custom image button as the nav bar's custom right view
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"email.png"]
style:UIBarButtonItemStyleBordered target:self action:#selector(action:)];
self.navigationItem.rightBarButtonItem = addButton;
[addButton release];
}
- (IBAction)action:(id)sender
{
// the custom icon button was clicked, handle it here
// change image here
}

Resources