14

I am trying to set a custom image for the back button that automatically gets place onto a navigation bar when a new view is pushed onto the stack.

I have tried adding the following in the viewDidLoad of the parent viewController:

[[UIBarButtonItem appearance] setBackButtonBackgroundImage:[UIImage imageNamed:@"BackButton.png"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];

I have also tried the following:

UIBarButtonItem *btn = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"BackButton.png"] style:UIBarButtonItemStyleBordered target:nil action:nil];
    self.navigationItem.backBarButtonItem = btn;

Using UIAppearance produces quite strange results: The BackButton image seems to be placed underneath the original title

Paul Morris
  • 1,763
  • 10
  • 33
  • 47
  • Was the `backBarButtonItem` approach successful, aside from the fact you have to do it every time? – Brian Nickel Jun 13 '12 at 17:49
  • No, neither way was successful. Ive ended up having to do it a hacky way by hiding the backButton and then setting a standard button and popping the views off the stack. Not ideal. – Paul Morris Jun 13 '12 at 17:52
  • Hmm... You could alternatively try setting the back button text to `@" "`, or setting the back button text color to transparent while still using the first approach. – Brian Nickel Jun 13 '12 at 18:23
  • this looks like the back button you created had already text on it... if that's so.. the approach is wrong... ios will expand the button to fit the back button text and will also put text on top of it – Heavy_Bullets Aug 25 '13 at 16:15

8 Answers8

37

try this code

UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom];  
UIImage *backBtnImage = [UIImage imageNamed:@"BackBtn.png"]  ;  
[backBtn setBackgroundImage:backBtnImage forState:UIControlStateNormal];  
[backBtn addTarget:self action:@selector(goback) forControlEvents:UIControlEventTouchUpInside];  
backBtn.frame = CGRectMake(0, 0, 54, 30);  
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithCustomView:backBtn] ;  
self.navigationItem.leftBarButtonItem = backButton;

then define goback method like this

- (void)goback
{
    [self.navigationController popViewControllerAnimated:YES];
}
Piyush Kashyap
  • 1,965
  • 1
  • 14
  • 16
  • That is pretty much exactly what I have done as a work around. I have hidden the backButton and used the leftBarButton to display my custom image then pop the view off the stack. It isn't ideal though. – Paul Morris Jun 13 '12 at 18:30
  • It is tested code,I have used it in many apps,try copy & paste my code ,it is possible that you might be doing something wrong,Please check it out again – Piyush Kashyap Jun 13 '12 at 18:33
  • My code is little different as I am initializing UIBarButtonItem with a custom view (i.e.) UIButton – Piyush Kashyap Jun 13 '12 at 18:36
  • I am not saying the code is wrong. I have resorted to using the same method. what I am saying is it isnt really the /correct/ way of doing it as iOS 5 has a UIApperance proprty to set a custom image for the back button. – Paul Morris Jun 13 '12 at 18:36
  • Ok,it's correct that IOS 5 has different way to set image for back button but they do not say that you cannot create a back button on your own, so I think you can use that code too :) – Piyush Kashyap Jun 13 '12 at 18:38
29

Since iOS 7.0 there is a new method in API backIndicatorImage. You can use it instead of setBackButtonBackgroundImage if you don't want the image to be stretched to fit the text (for example if you want a fixed size custom back arrow). Here's an example in swift:

let image = UIImage(named: "back_button")
    
UINavigationBar.appearance().backIndicatorImage = image
UINavigationBar.appearance().backIndicatorTransitionMaskImage = image

You can hide the text from the button using this trick:

UIBarButtonItem.appearance().setBackButtonTitlePositionAdjustment(UIOffsetMake(0, -66), for: .default)

for Swift 5

var backButtonImage = UIImage(named: "back_button")

backButtonImage = backButtonImage?.stretchableImage(withLeftCapWidth: 15, topCapHeight: 30) UIBarButtonItem.appearance().setBackButtonBackgroundImage(backButtonImage, for: .normal, barMetrics: .default)

Simran Singh
  • 445
  • 6
  • 8
Konstantine Kalbazov
  • 2,643
  • 26
  • 29
  • You can also hide the text on the StoryBoard, In the controller that perform the sugue(push) you can set to blank space (" " without quotes) the back button title of the NavigationBar (property on the attribute inspector). – José Roberto Abreu Jun 27 '16 at 19:17
  • How to make the image fit? Right now I'm getting a big image that doesn't quite fit... – Sylvan D Ash Jul 15 '17 at 11:33
23

This is the code I'm using and works perfectly in my own iOS 5 app. This code is from application:didFinishLaunchingWithOptions: in the app delegate:

UIImage * backButtonImage = [UIImage imageNamed: @"back-button-image"];
backButtonImage = [backButtonImage stretchableImageWithLeftCapWidth: 15.0 topCapHeight: 30.0];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage: backButtonImage forState: UIControlStateNormal barMetrics: UIBarMetricsDefault];

You'll may need to use a stretchable image, where the left facing "point" is the left cap


or in Swift

let backButtonImage = UIImage(named: "back-button-image")
backButtonImage = backButtonImage?.stretchableImageWithLeftCapWidth(15, topCapHeight: 30)
UIBarButtonItem.appearance().setBackButtonBackgroundImage(backButtonImage, forState: .Normal, barMetrics: .Default)
Ashley Mills
  • 50,474
  • 16
  • 129
  • 160
  • Use backButtonImage = [backButtonImage resizableImageWithCapInsets:UIEdgeInsetsZero]; instead of stretchableImageWithLeftCapWidth as this method is deprecated. – Ramis Sep 15 '15 at 07:35
5

iOS 8+ Swift version (can be converted to ObjC with not much effort). This method preserves all advantages and behaviour of navigation bar related to standard back button (animation on pushing/popping, interactive pop gesture, etc)

// Call the code ONE TIME somewhere on app launch to setup custom back button appearance
UINavigationBar.appearance().tintColor = UIColor.blackColor()
// If you need custom positioning for your back button, in this example button will be 1 px up compared to default one
// Also only vertical positioning works, for horizontal add offsets directly to the image
let backImageInsets = UIEdgeInsetsMake(0, 0, -1, 0)
// Get image, change of rendering (so it preserves offsets made in file), applying offsets
let backImage = UIImage(named: "YourButtonImageAssetFileName")?.imageWithRenderingMode(.AlwaysOriginal).imageWithAlignmentRectInsets(backImageInsets)
// Setting images
UINavigationBar.appearance().backIndicatorImage = backImage
UINavigationBar.appearance().backIndicatorTransitionMaskImage = backImage

// Call EACH TIME BEFORE pushing view controller if you don't need title near your back button arrow
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .Plain, target: nil, action: nil)
// Push controller
self.navigationController?.pushViewController(vc, animated: true)
Andrei Konstantinov
  • 6,971
  • 4
  • 41
  • 57
3

Try the code below, it works for me:

[[UINavigationBar appearance] setBackIndicatorImage:[UIImage imageNamed:@"back-button-image"]];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"back-button-image"]];
pragmus
  • 3,513
  • 3
  • 24
  • 46
2

This code worked for Swift 5 and XCode 10 :

Add this code to AppDelegate's func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool to Apply to all of your view controllers:

//Custom back button
    let barButtonItemAppearance = UIBarButtonItem.appearance()
    barButtonItemAppearance.setTitleTextAttributes([NSAttributedStringKey.foregroundColor: UIColor.clear], for: .normal)
    barButtonItemAppearance.setTitleTextAttributes([NSAttributedStringKey.foregroundColor: UIColor.clear], for: .highlighted)
    let backImg: UIImage = UIImage(named: "back")!
    barButtonItemAppearance.setBackButtonBackgroundImage(backImg, for: .normal, barMetrics: .default)

    let image = UIImage()

    UINavigationBar.appearance().backIndicatorImage = image
    UINavigationBar.appearance().backIndicatorTransitionMaskImage = image
mkhoshpour
  • 845
  • 1
  • 10
  • 22
2

Write this method in common class and call it from the controller in which you want to draw.

(UIButton *)drawNavigationBarBackButton:(UIViewController*)viewController 
{

    UIImage *navBackImage = nil;

    UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
     navBackImage = [UIImage imageNamed:@"Back_Button"];

    [backButton setImage:navBackImage forState:UIControlStateNormal];
    UIBarButtonItem *leftButton = [[UIBarButtonItem alloc] initWithCustomView:backButton];
    viewController.navigationItem.leftBarButtonItem = leftButton;

    return backButton;
}



call from controller class and setting action

- (void)viewDidLoad {

[super viewDidLoad];

 UIButton *backButton = [CommonClass drawNavigationBarBackButton:self];
    [backButton addTarget:self action:@selector(onTapBackButton) forControlEvents:UIControlEventTouchUpInside];
}

- (void)onTapBackButton 
{
    [self.navigationController popViewControllerAnimated:YES];
}
Subodh Kumar
  • 229
  • 3
  • 8
1

This worked for me:

let button1 = UIBarButtonItem(
    image: #imageLiteral(resourceName: "back_arrow"), 
    style: .plain, 
    target: self, 
    action: #selector(self.backBtnTapped(_:))
)
self.navigationItem.leftBarButtonItem  = button1
Dov Benyomin Sohacheski
  • 7,133
  • 7
  • 38
  • 64
Y0gita
  • 531
  • 4
  • 5