5

I want to set custom back bar button for all controllers in the app. I tried using this:

[[UIBarButtonItem appearance] setBackButtonBackgroundImage:backButtonImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];

It sets the image. That's OK. But, what i really want to do is that, i just want a custom button for back bar button which does not contain any title etc. The code above works, but it adds automated titles and resizes the back bar button item. My need is to have a fixed frame, no-title back bar button item for all controllers in the app.

rordulu
  • 412
  • 4
  • 12

6 Answers6

5

I've resolved it. Just make a category over UIViewController and import it in prefix.pch file. Then write a method: customViewWillAppear: and swizzle it with viewWillAppear method:

+(void)load{

Method viewWillAppear = class_getInstanceMethod(self, @selector(customViewWillAppear:));

Method customViewWillAppear = class_getInstanceMethod(self, @selector(viewWillAppear:));
method_exchangeImplementations(viewWillAppear, customViewWillAppear);

}

Add the above method to that category class. Then implement your customViewWillAppear method like this:

-(void)customViewWillAppear:(BOOL)animated{
    [self customViewWillAppear:animated];
    if([self.navigationController.viewControllers indexOfObject:self] != 0  && !self.navigationItem.hidesBackButton){
        UIBarButtonItem *cancelBarButton = nil;
        UIButton* cancelButton = [UIButton buttonWithType:UIButtonTypeCustom];
        [cancelButton addTarget:self action:@selector(popViewControllerWithAnimation) forControlEvents:UIControlEventTouchUpInside];
        [cancelButton setBackgroundImage:[UIImage imageNamed:@"yourImage.png"] forState:UIControlStateNormal];
        [cancelButton sizeButtonToFit];

        cancelBarButton = [[UIBarButtonItem alloc] initWithCustomView:cancelButton];

        NSMutableArray * leftButtons = [NSMutableArray arrayWithObject:cancelBarButton];
        [leftButtons addObjectsFromArray:self.navigationItem.leftBarButtonItems];
        [self.navigationItem setLeftBarButtonItem:nil];
        [self.navigationItem setLeftBarButtonItems:leftButtons];
    }

    [self.navigationItem setHidesBackButton:YES];
}
-(void)popViewControllerWithAnimation{
    [self.navigationController popViewControllerAnimated:YES];
}

Now, for every controller in your code, you have a custom back button. This took me a lot of time to implement and figure out. Hope it'll help you guys all too.

EDIT: Please use the following code to support iOS7> back swipe feature;

UIImage *image = [UIImage imageForName:@"some_image"];
navBar.backIndicatorImage = [image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
navBar.backIndicatorTransitionMaskImage = [image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];

Create a base view controller and add the following code;

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
}
rordulu
  • 412
  • 4
  • 12
  • Thanks a lot for this code! The only issue I've found so far is that now it's not possible to drag the view controller from the left edge to the right to pop it from the navigation stack – skornos Sep 10 '15 at 11:27
  • Yes that's true for devices which have iOS 7 or higher. – rordulu Sep 10 '15 at 13:57
  • ok, I have found another solution for this issue, you need to set the view controller as the delegate for interactive pop gesture recognizer with `self.navigationController.interactivePopGestureRecognizer.delegate = (id)self;` inside `customViewWillAppear` – skornos Sep 11 '15 at 12:35
  • But this one is a hack. I prefer using appearance proxy: http://stackoverflow.com/a/20330647/1092167 – skornos Sep 11 '15 at 12:59
  • This appearance proxy doesn't prevent the navigation bar to show the "back" title. – rordulu Sep 12 '15 at 13:20
  • that is why I am using another appearance proxy for UIBarButtonItem (disadvantage is that is applies to all UIBarButtonItems): http://stackoverflow.com/a/21015005/1092167 – skornos Sep 12 '15 at 14:03
  • And If anyone wanted to display some text in UIBarButtonItem, you can override ViewController's `viewWillAppear:animated` with `[self.navigationItem.leftBarButtonItem:setTitleTextAttributes]` – skornos Sep 16 '15 at 13:10
2

I've had a similar problem before and I've searched everywhere for a solution.

The only one I've found, which works for your problem was to implement in EVERY view controller a UILeftBarButton which does the popping.

You can change the background image the way you're doing, but if you set the text to nil or empty text (""), you're button just won't show up.

You also can't change the View of the UIBackBarButton, only it's text (so no custom button).

Lord Zsolt
  • 6,492
  • 9
  • 46
  • 76
  • I've dozens of controllers, this operation is just too expensive for me to do. – rordulu Aug 07 '13 at 09:07
  • You won't be able to change the back button's background AND remove text (Apple did this while having localization in mind, since you shouldn't add the text to the image, instead add it from code) – Lord Zsolt Aug 07 '13 at 09:10
2

What i did, was set the backbutton title label alpha to zero, using appearance proxy.

Rob van der Veer
  • 1,148
  • 1
  • 7
  • 20
  • can you share any code? I don't think you can access the titlelabel of a uibarbuttonitem. Even if you do, it will only set the visibility of the title, backbutton will still changed its frame as resized by title. – rordulu Aug 07 '13 at 09:11
  • I'm not behind my desk, but if you adjust fontsize to silly 0.1 it will be undetectable. Use `[[UIBarButtonItem appearance] setTitleTextAttributes ...` – Rob van der Veer Aug 07 '13 at 09:41
1

You could write a category like this,

//
//  UINavigationItem+BarAditions.h
//
//  Created by Satheeshwaran on on 7/5/13.
//


#import <Foundation/Foundation.h>


@interface UINavigationItem (PersonaAddition)

- (void)setCustomBackButton;

@end


//
//  UINavigationItem+BarAditions.m
//
//  Created by Satheeshwaran on on 7/5/13.
//

#import "UINavigationItem+PersonaAddition.h"


@implementation UINavigationItem (PersonaAddition)

- (void)setCustomBackButton 
{
    //customize ur back button here.
    UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
    backButton.frame=CGRectMake(0, 0, 60, 30);
    [backButton addTarget:target action:@selector(didPressLeftItem:) forControlEvents:UIControlEventTouchUpInside];
    UIBarButtonItem *barItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
    self.leftBarButtonItem = barItem;    

}

and import this category in all ur files and call the setCustomBackButton in all ur classes. This works fine for me, even in iOS 7.

In ViewDidLoad of all ur classes.

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self.navigationItem setCustomBackButton];

}
Satheesh
  • 10,998
  • 6
  • 50
  • 93
1

My code in ViewDidLoad:

// Set back button image
if ([[self.navigationController viewControllers] objectAtIndex:0] != self) {
    UIButton *btnBack = [UIButton buttonWithType:UIButtonTypeCustom];
    btnBack.frame = CGRectMake(0,0,38,30);
    [btnBack setBackgroundImage:[UIImage imageNamed:@"NavigationBarBackButton"] forState:UIControlStateNormal];
    [btnBack addTarget:self.navigationController action:@selector(popViewControllerAnimated:) forControlEvents:UIControlEventTouchUpInside];
    UIBarButtonItem *leftBtn = [[UIBarButtonItem alloc] initWithCustomView:btnBack];
    [self.navigationItem setLeftBarButtonItem:leftBtn];
}
Timur Bernikovich
  • 5,660
  • 4
  • 45
  • 58
0

try this code:

UIButton *tempbtn = [[UIButton alloc] init];
[tempbtn setImage:[UIImage imageNamed:@"testbtn.png"] forState:UIControlStateNormal];
UIBarButtonItem *temp = [[UIBarButtonItem alloc] initWithCustomView:tempbtn];
self.navigationItem.rightBarButtonItem = temp;
aahsanali
  • 3,537
  • 23
  • 21
NHS
  • 409
  • 2
  • 7
  • you need not set the title of UIBarbuttonsystemItems.And also you need not set the title for images. – NHS Aug 07 '13 at 09:09