iOS 7 custom back button - ios

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

Related

how to override the functionality of the navigation bar created by pushViewController

I have a view controller, which is presented using pushViewController. This methods presents the view controller with a navigation bar, with a back button on the left side (this is a cocoa method, created by apple).
I want the back button to do just what it does right now, but I also want to tweak some things with it's animation.
How can I add code which works between the button being pressed and the actual animation and return?
thank you.
I do similar stuff by adding my custom back button like this:
UIImage *backArrow = [UIImage imageNamed:backButtonImage];
UIButton *aBackButton = [UIButton buttonWithType:UIButtonTypeSystem];
CGSize aBackButtonTextSize = [#"Back" sizeWithFont:[UIFont systemFontOfSize:17]];
aBackButton.frame = CGRectMake(0.0, 0.0, aBackButtonTextSize.width + backArrow.size.width, self.navigationController.navigationBar.frame.size.height);
aBackButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
SEL backSelector = NSSelectorFromString(#"backAction");
[aBackButton addTarget:self action:backSelector forControlEvents:UIControlEventTouchUpInside];
[aBackButton setTitle:#"Back" forState:UIControlStateNormal];
[aBackButton setImage:backArrow forState:UIControlStateNormal];
[aBackButton setExclusiveTouch:YES];
aBackButton.titleLabel.font = [UIFont systemFontOfSize:17];
UIBarButtonItem *aLeftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:aBackButton];
UIBarButtonItem *aNegativeSpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
aNegativeSpacer.width = -8;
self.navigationItem.leftBarButtonItems = #[aNegativeSpacer, aLeftBarButtonItem];
And then handle user taps and add my own stuff here:
- (void)backAction {
// My own handlings
}

backBarButtonItem not getting displayed

I know viewcontroller's navigation item's backBarButtonItem gets displayed when another view controller is pushed on stack and this is 2nd viewcontroller from top.
I have viewcontroller A which have following in viewDidLoad
{
[super viewDidLoad];
self.navigationController.navigationBarHidden = NO;
[self.navigationItem setBackBarButtonItem:[UIBarButtonItem itemWithImageNamed:#"ic_header_slide" selectedImage:nil target:nil action:nil]];
}
When I push viewcontroller B, this custom back button is not getting displayed, instead I see default back button which iOS creates.
A extends UITableViewController and B extends UIViewController. I am not setting leftBarButtonItem, leftBarButtonItems, rightBarButtonItem, rightBarButtonItems in any of these navigationItem.
EDIT
I have read about setting leftBarButtonItems. setting leftbarbuttonitems on B works. but I think setting backBarButtonItem on A is correct way of doing that. It is also mentioned in documentation but not working in my case. I want to ask whether there is bug in backBarButtonItem or I have some misunderstanding the way it works and I am not doing it correctly.
To hide the default back button of the navigation bar use,
self.navigationItem.hidesBackButton=TRUE;
Also use the following method to add the custom BarButtons,
- (NSArray*)getLeftNavButtons:(NSString*)image andTarget:(id)target andFrame:(CGRect)frame andSpace:(int)fixedSpace
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = frame;
button.clipsToBounds = YES;
[button setBackgroundImage:[UIImage imageNamed:image] forState:UIControlStateNormal];
[button addTarget:target action:#selector(leftNavBtnClicked) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *barButton = [[UIBarButtonItem alloc]initWithCustomView:button];
if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7"))
{
UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace
target:nil action:nil];
negativeSpacer.width = fixedSpace;
return #[negativeSpacer,barButton];
}
else{
return #[barButton];
}
return #[barButton];
}
Just override the default
self.navigationItem.hidesBackButton = YES;
UIBarButtonItem *back = [[UIBarButtonItem alloc]init];
back.title = #"Pick Me";
back.image = #"Your image";
[self.navigationItem setLeftBarButtonItem:back];
Set Right Bar Button Item
self.navigationItem.leftBarButtonItem=[[UIBarButtonItem alloc]init];
UIImage *img1=[UIImage imageNamed:#"edit"];
CGRect frameimg1 = CGRectMake(0, 0, img1.size.width, img1.size.height);
UIButton *signOut=[[UIButton alloc]initWithFrame:frameimg1];
[signOut setBackgroundImage:img1 forState:UIControlStateNormal];
[signOut addTarget:self action:#selector(btnEditClicked:)
forControlEvents:UIControlEventTouchUpInside];
// [signOut setShowsTouchWhenHighlighted:YES];
UIBarButtonItem *barButton=[[UIBarButtonItem alloc]initWithCustomView:signOut];
self.navigationItem.rightBarButtonItem=barButton;
Set Left Bar Button Item
UIImage *img11=[UIImage imageNamed:#"home"];
CGRect frameimg11 = CGRectMake(0, 0, img11.size.width, img11.size.height);
UIButton *signOut1=[[UIButton alloc]initWithFrame:frameimg11];
[signOut1 setBackgroundImage:img11 forState:UIControlStateNormal];
[signOut1 addTarget:self action:#selector(showLeftMenuPressed:)
forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *barButton1=[[UIBarButtonItem alloc]initWithCustomView:signOut1];
self.navigationItem.leftBarButtonItem=barButton1;
self.navigationController.navigationBar.barTintColor=ColorNav;
self.navigationController.navigationBar.translucent=FALSE;
Set Navigation Title & Color
self.title = titletext;
[[[self navigationController] navigationBar]setTitleTextAttributes:#{NSForegroundColorAttributeName: textColor}];

How do I set a custom view back button

In View Controller A, in viewDid Load I have this:
UIImage * backButtonImage = [UIImage imageNamed: #"bar-arrow"];
UIBarButtonItem* b = [[UIBarButtonItem alloc] initWithImage:backButtonImage style:UIBarButtonItemStylePlain target:nil action:nil];
self.navigationItem.backBarButtonItem = b;
Still In the next view controller's nav. bar I get this:
In other words - the original back arrow of IOS7 still shows. cannot get rid of it!!
Generally hide default back button from NativationBar:
[self.navigationItem setHidesBackButton:YES animated:YES];
[self.navigationItem setHidesBackButton:YES];
and do following code:
UIButton *BackButton = [UIButton buttonWithType:UIButtonTypeCustom];
[BackButton setImage:[UIImage imageNamed:#"bar-arrow"] forState:UIControlStateNormal];
[BackButton addTarget:self action:#selector(prevButtonAction)forControlEvents:UIControlEventTouchUpInside];
[BackButton setFrame:CGRectMake(0, 0, 12, 20)];// set as par your need
UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithCustomView:BackButton];
self.navigationItem.leftBarButtonItem = barButton;
-(void)prevButtonAction
{
[self.navigationController popViewControllerAnimated:YES];
}
If you want to keep the back button functionality but just change the chevron image (and if you are targeting only iOS 7.0 and above), you can change the image like so:
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationController.navigationBar.backIndicatorImage = [UIImage imageNamed:#"bar-arrow"];
self.navigationController.navigationBar.backIndicatorTransitionMaskImage = [UIImage imageNamed:#"bar-arrow-transition-mask"];
}
Or if you were to subclass a UINavigationController, which I would suggest:
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationBar.backIndicatorImage = [UIImage imageNamed:#"bar-arrow"];
self.navigationBar.backIndicatorTransitionMaskImage = [UIImage imageNamed:#"bar-arrow-transition-mask"];
}
... where the "bar-arrow-transition-mask" image is an image representing the visibility of the other image, "bar-arrow". In this mask image, pixels with an alpha of zero will completely hide the moving title during transitions, while pixels with an alpha of one will completely show the moving title.
While this will require you to make a new image, it should be very quick and easy to make, and this solution preserves all navigation bar functionality without messing with the leftBarButtonItem or leftBarButtonItems and adding in manual calls to popViewControllerAnimated:.

Changing Navigatiobar back button action in ios6/ ios 7 compatible applications

I have a navigation controller application, and I need to set the custom action for the navigation back bar button. Tried some workarounds and not yet to find a solution.
Tried
UIBarButtonItem *backBarItem = self.navigationItem.leftBarButtonItem;
backBarItem.target = self;
backBarItem.action = #selector(popToHomeViewController);
Result : No effect. Back button pops to just previous viewController in navigation stack
UIBarButtonItem *customBarItem = [[UIBarButtonItem alloc] initWithTitle:backBarItem.title style:backBarItem.style target:self action:#selector(popViewController)];
self.navigationItem.leftBarButtonItem = customBarItem;
Result : No effect. Back button pops to just previous viewController in navigation stack
UIBarButtonItem *customBarItem = [[UIBarButtonItem alloc] initWithTitle:#"back" style:backBarItem.style target:self action:#selector(popViewController)];
self.navigationItem.leftBarButtonItem = customBarItem;
Result:Now my selector got invoked perfectly and navigated to desired viewController. Here the issue is that the back button not as like as the native back button. It is not having the bold "<" character as I have not mentioned it. If added < character it needs to be changed for ios 6 compatibility.
Any better solution to ensure ios 6 and ios 7 compatible navigation back button with custom selector?
Try this simple example will help you..
- (void)viewDidLoad {
[super viewDidLoad];
UIImage *buttonImage = [UIImage imageNamed:#"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(back) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *customBarItem = [[UIBarButtonItem alloc] initWithCustomView:button];
self.navigationItem.leftBarButtonItem = customBarItem;
[customBarItem release];
}
-(void)back {
[self.navigationController popViewControllerAnimated:YES];
}
Make sure you have an button image with the size of a navigation bar back button in your resource folder with name back.png
Feel free if any other assistance is required.
Happy Coding!!!!!
Try this
viewController.navigationItem.hidesBackButton = YES;
//set custom image to button if needed
UIImage *backButtonImage = [UIImage imageNamed:#"back"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setImage:backButtonImage forState:UIControlStateNormal];
button.frame = CGRectMake(0, 0, backButtonImage.size.width, backButtonImage.size.height);
[button addTarget:viewController action:#selector(back) forControlEvents:UIControlEventTouchUpInside];
UIView *backButtonView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, backButtonImage.size.width, backButtonImage.size.height)];
[backButtonView addSubview:button];
UIBarButtonItem *customBarItem = [[UIBarButtonItem alloc] initWithCustomView:backButtonView];
viewController.navigationItem.leftBarButtonItem = customBarItem;
and in back method you can customise
- (void)back {
//Your code
}

Why won't this UIButton display as my leftBarButtonItem, UIBarButtonItem seems to be set properly

In my RootViewController, I set an image as a rightBarButtonItem exactly like this, but in this second ViewController I created, this "back" button won't display. I run it on the simulator and on my iPhone and I can tap the area where the button should be showing up and the goBack{} method is being called. Just the image(s) aren't showing. Any thoughts?
Also, as far as the images go, I'm exporting them the exact same way I've been exporting all my other images. All the others have been working fine. It doesn't seem like a problem with the images, as I've substituted the back button images for several others and none of them are displaying. I'm pretty sure I'm creating these images in the right order. Anyway, any help would be much appreciated.
From my NewGameViewController.m file:
- (void)viewDidLoad {
[super viewDidLoad];
CGFloat startingPoint = 0.0;
CGRect bounds = self.view.bounds;
bounds.origin.y = startingPoint;
bounds.size.height -= startingPoint;
self.ivarNewGameTableView = [[UITableView alloc] initWithFrame:bounds style:UITableViewStyleGrouped];
self.ivarNewGameTableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
self.ivarNewGameTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:self.ivarNewGameTableView];
UIImageView *backgroundImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"background.png"]];
self.ivarNewGameTableView.backgroundView = backgroundImageView;
UIImage *mainNavBar = [UIImage imageNamed:#"startNewGameNavBar.png"];
UIImageView *navBarView = [[UIImageView alloc] initWithImage:mainNavBar];
[navBarView setFrame:CGRectMake(0.0, 0.0, 320.0, 44.0)];
[self.navigationController.navigationBar addSubview:navBarView];
UIButton *back = [UIButton buttonWithType:UIButtonTypeCustom];
// neither one of the following lines work. I know they're supposed to be identical, but
// seen Xcode be picky.
back.frame = CGRectMake(0.0, 0.0, 54.0, 36.0);
// [back setFrame:CGRectMake(0.0, 0.0, 54.0, 36.0)];
[back addTarget:self action:#selector(goBack) forControlEvents:UIControlEventTouchUpInside];
[back setImage:[UIImage imageNamed:#"back.png"] forState:UIControlStateNormal];
[back setImage:[UIImage imageNamed:#"backTouched.png"] forState:UIControlStateHighlighted];
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithCustomView:back];
self.navigationItem.leftBarButtonItem = backButton;
}
-(void)goBack {
NSLog(#"goBack called.");
// [self.ivarNewGameTableView pushViewController:];
}
If you're in a navigation stack the back button item is set for you. Use backBarButtonItem correctly do get the behaviour you like.
On your main view controller that you want to come back to you need to overload the backBarButton item.
UIBarButtonItem *newBackButton = [[UIBarButtonItem alloc] initWithTitle: #"Back" style: UIBarButtonItemStyleBordered target: nil action: nil];
self.navigationItem.backBarButtonItem = newBackButton;
Some code samples on github
Ok. Finally figured this out. The problem was with this line in the creation of my New Game View Navigation Bar:
[self.navigationController.navigationBar addSubview:navBarView];
I did this in my Root View's Navigation Bar too. It now looks like this, in both files:
UIImage *startNewGameNavBar = [UIImage imageNamed:#"startNewGameNavBar.png"];
// with the key line being this one
[self.navigationController.navigationBar setBackgroundImage:startNewGameNavBar forBarMetrics:UIBarMetricsDefault];
In noob-speak, as I wish more programmers would use for noobs like me, if you're having problems with buttons disappearing behind the your custom navigation bar, and you're using code that looks like my example, it's because you're not supposed to be adding it as a subview. This puts it on top of everything else. Instead, change the navigation bar's background image and, of course, the image winds up in the...(drumroll, please)...background. (end drumroll)
As for the adding of the button images, I just created a UIButton with custom type, set the images for the normal and highlighted states, set the button to call my goBack method, set its frame size, and assigned it to the navigationItem's leftBarButtonItem property:
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[backButton setImage:[UIImage imageNamed:#"back.png"] forState:UIControlStateNormal];
[backButton setImage:[UIImage imageNamed:#"backTouched.png"] forState:UIControlStateHighlighted];
[backButton addTarget:self action:#selector(goBack) forControlEvents:UIControlEventTouchUpInside];
[backButton setFrame:CGRectMake(0.0, 0.0, 54.0, 36.0)];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];

Resources