Swift Custom NavBar Back Button Image and Text
Asked Answered
C

18

76

I need to customise the look of a back button in a Swift project.

Here's what I have: Default Back Button

Here's what I want: Custom Back Button

I've tried creating my own UIBarButtonItem but I can't figure out how to get the image to be beside the text, rather than as a background or a replacement for the text.

let backButton = UIBarButtonItem(title: "Custom", style: .Plain, target: self, action: nil    )
//backButton.image = UIImage(named: "imageName") //Replaces title
backButton.setBackgroundImage(UIImage(named: "imageName"), forState: .Normal, barMetrics: .Default) // Stretches image
navigationItem.setLeftBarButtonItem(backButton, animated: false)
Clone answered 31/5, 2016 at 18:15 Comment(1)
@Florrie You mean make a UIView and add a Label and ImageView and set that?Clone
F
109

You can do something like that:

let yourBackImage = UIImage(named: "back_button_image")
self.navigationController?.navigationBar.backIndicatorImage = yourBackImage
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = yourBackImage
self.navigationController?.navigationBar.backItem?.title = "Custom"

Your image will only have one color though

Florrie answered 31/5, 2016 at 20:8 Comment(6)
Mostly worked, the title of the button didn't change. let yourBackImage = UIImage(named: "back_button_image") self.navigationController?.navigationBar.backIndicatorImage = yourBackImage self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = yourBackImage self.navigationController?.navigationBar.backItem?.title = "Custom"Clone
My back button ends up in the center of the screen? Any thoughts?Ottar
@lusus_vir Did you end up fixing this issue? I'm seeing the same result.Claver
I there, if I use this code, this will change every back icon for the controllers that belongs to the same navigation controller, so how can I manage this behavior to only change the back button image in one controller in the same navigation controller? I try to put in a buffer the original content of self.navigationController?.navigationBar.backIndicatorImage, but its nullIllaffected
I've missed "backIndicatorTransitionMaskImage". Thank you, you saved me a lot of time.Orchidaceous
In your assets, if you set your image "Original Image" as render mode, your image keep its colors!Adventurous
I
44

Note: Please remember that the back button belongs to the the source ViewController and not to the destination ViewController. Thus, the modification needs to be done in the source VC, which is reflected to all the view in the navigation controller

Code Snippet:

let backImage = UIImage(named: "icon-back")

self.navigationController?.navigationBar.backIndicatorImage = backImage

self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = backImage

/*** If needed Assign Title Here ***/
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: UIBarButtonItemStyle.plain, target: nil, action: nil)
Isochor answered 15/9, 2016 at 10:34 Comment(3)
it works, BUT, how to set the image frame. Just with this code the whole image goes out of frame.Schweinfurt
@Annjawn image can be corrected this way: UIImage(named: "icon-back")?.stretchableImage(withLeftCapWidth: 15, topCapHeight: 30), as shown in Idan's answer belowPriesthood
Didn't remove the text also :(Mcalpin
B
33

swift 4

In my case, I needed to have only the image of the button, without any text. I hope this will be useful to someone.

let imgBackArrow = UIImage(named: "back_arrow_32")

navigationController?.navigationBar.backIndicatorImage = imgBackArrow
navigationController?.navigationBar.backIndicatorTransitionMaskImage = imgBackArrow

navigationItem.leftItemsSupplementBackButton = true
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: self, action: nil)

For iOS 12 you can do

func setNavigationBar() {

    self.navigationItem.setHidesBackButton(true, animated:false)

    //your custom view for back image with custom size
    let view = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
    let imageView = UIImageView(frame: CGRect(x: 10, y: 10, width: 20, height: 20))

    if let imgBackArrow = UIImage(named: "icn_back_arrow") {
        imageView.image = imgBackArrow
    }
    view.addSubview(imageView)

    let backTap = UITapGestureRecognizer(target: self, action: #selector(backToMain))
    view?.addGestureRecognizer(backTap)

    let leftBarButtonItem = UIBarButtonItem(customView: view ?? UIView())
    self.navigationItem.leftBarButtonItem = leftBarButtonItem
}

@objc func backToMain() {
    self.navigationController?.popViewController(animated: true)
}
Biak answered 5/1, 2018 at 16:11 Comment(2)
Didn't remove the text on iOS12Mcalpin
The ios 12 method ruins the interactive swipe to dismissMaharani
D
14

For setting custom back bar button and remove text from back bar button, FROM STORYBOARD only, without any coding.


For setting custom back button and remove text from back button

RESULT:

enter image description here

Dedicated answered 13/1, 2020 at 17:53 Comment(1)
The method is invalid ,cannot make it happenOverall
R
11

For the back button image:

  • By this tutorial: (but didn't work for me)

    UINavigationBar.appearance().backIndicatorImage = UIImage(named: "imageName")
    
  • But this stack answer: (worked for me)

    var backButtonImage = UIImage(named: "back-button-image")
    backButtonImage = backButtonImage?.stretchableImage(withLeftCapWidth: 15, topCapHeight: 30)
    UIBarButtonItem.appearance().setBackButtonBackgroundImage(backButtonImage, for: .normal, barMetrics: .default)
    

And for the font, assuming you want the font to match for the whole navigation bar:(currently in use)

if let font = UIFont(name: "Avenir-Book", size: 22) {
  UINavigationBar.appearance().titleTextAttributes = [NSFontAttributeName: font]
}
Remus answered 31/5, 2016 at 20:3 Comment(1)
Also add UINavigationBar.appearance().backIndicatorTransitionMaskImage = UIImage(named: "imageName"), and it will work.Testify
P
11

Having a button in Navigation Bar with Image AND Text is quite hard. Especially after they have introduced a new headache with UIBarButtonItem position in iOS 11: iOS 11 - UIBarButtonItem horizontal position

You can make either button with image or a button with text, but not a button with both of those. I even tried two UIBarButtonItems together, one with image and other with text - it still doesn't look good at all and their UIStackView can't be easily accessed for modification.

Unexpectedly I found a plain simple solution:

1) design the button as view in Interface Builder. In my case it is inside target UIViewController and accessible via IBOutlet for simplicity

2) set Leading Space constraint for the image to be negative, you might also want to set view's background color to .clear.

enter image description here

3) use it:

@IBOutlet var backButtonView: UIView!

override func viewDidLoad() {
    super.viewDidLoad()

    let backButton = UIBarButtonItem(customView: self.backButtonView)
    self.backButtonView.heightAnchor.constraint(equalToConstant: 44).isActive = true // if you set more than you'll get "Unable to simultaneously..."
    self.backButtonView.widthAnchor.constraint(equalToConstant: 75).isActive = true
    self.navigationItem.leftBarButtonItem = backButton
}

That's it. No need to use the trick with negative spacer for iOS 10 or the trick with imageInsets for iOS 11 (which works only if you have image and doesn't work for image+text, BTW).

enter image description here

Priesthood answered 23/1, 2018 at 22:1 Comment(1)
In my case arrow hit area is working a bit strange (only the right part can be touched). And also you have 2 separate buttons so it doesn't work the same, it only look the same.Diastyle
M
10

I have tried all the above and all make the custom image without changing the text The only one worked for me is from this answer

let backBTN = UIBarButtonItem(image: UIImage(named: "Back"), 
                              style: .plain, 
                              target: navigationController, 
                              action: #selector(UINavigationController.popViewController(animated:)))
navigationItem.leftBarButtonItem = backBTN
navigationController?.interactivePopGestureRecognizer?.delegate = self
Mcalpin answered 4/1, 2019 at 23:57 Comment(1)
This is great, because this way you can set a custom selector as well to intercept the tap. Brilliant :-)Resound
C
6

I know it was answered. Here you can set title, image and target.

    let view = UIView()
    let button = UIButton(type: .system)
    button.setImage(UIImage(named: "backArrow_theme"), for: .normal)
    button.setTitle("Back to workflow", for: .normal)
    button.addTarget(self, action: #selector(onBackButton(_:)), for: .touchUpInside)
    button.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: -10)
    button.sizeToFit()
    view.addSubview(button)
    view.frame = button.bounds
    navigationItem.leftBarButtonItem = UIBarButtonItem(customView: view)
Chinaware answered 11/8, 2021 at 13:54 Comment(2)
In my case, I dont need text for backbutton. Just replacing the text with blank space, didn't fixed the issue completely. Back button is still blue. I have used the following code to get the desired color. navigationController?.navigationBar.barTintColor = .black navigationController?.navigationBar.tintColor = .blackWanonah
Perfect! Much more flexible like this.Lelia
S
5

swift 3

    extension UIViewController {

        func setBackButton(){
            let yourBackImage = UIImage(named: "backbutton")
            navigationController?.navigationBar.backIndicatorImage = yourBackImage
            navigationController?.navigationBar.backIndicatorTransitionMaskImage = yourBackImage
        }

    }
Samathasamau answered 28/6, 2017 at 16:0 Comment(0)
S
5

This worked for me on iOS 13 using swift 5. Just hide the original back button and add a new navigation left bar button item with an action.

navigationItem.hidesBackButton = true
navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "backBtn"), style: .plain, target: self, action: #selector(back(sender:)))

@objc func back(sender: UIBarButtonItem) {
    self.navigationController?.popViewController(animated:true)
}
Samson answered 20/1, 2020 at 14:45 Comment(1)
This is simple and works, but not perfect. The leftBarButtonItem will be a bit to the right of the back buttons original position. If this is acceptable, then go ahead and use it.Fulbright
S
3

iOS13 And Later, Try to use UINavigationBarAppearance

let appearance = UINavigationBarAppearance()
// set back image
appearance.setBackIndicatorImage(UIImage(named: "back_icon"), transitionMaskImage: UIImage(named: "back_icon"))

// set appearance to one NavigationController
let navVC = UINavigationController()
navVC.navigationBar.standardAppearance = appearance
navVC.navigationBar.scrollEdgeAppearance = appearance

// or you can config for all navigation bar
UINavigationBar.appearance().standardAppearance = appearance
UINavigationBar.appearance().scrollEdgeAppearance = appearance

back title base on you Viewcontroller

viewController.navigationItem.backButtonTitle = "your back title"
Saying answered 21/6, 2022 at 5:45 Comment(1)
This works. You can easily change the back button image without loosing its properties. Note:- If you want white nav bar , only apply appearance to navigationBar.standapperanceFrication
S
2

Just replace the backButton with a custom rightBarButtonItem

let backImage = UIImage(named: "BackBtn")?.withRenderingMode(.alwaysOriginal)
    navigationItem.leftBarButtonItem = UIBarButtonItem(image: backImage, style: .plain, target: self, action: #selector(popnav))

    @objc func popnav() {
    self.navigationController?.popViewController(animated: true)
}
Scop answered 1/11, 2018 at 14:13 Comment(0)
S
2

Just in case someone need to change all Back buttons color or font with Swift5. UIBarButtonItem.appearance().tintColor = .red

Add this to AppDelegate.swift file.

import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    
    // Override point for customization after application launch.        
    UIBarButtonItem.appearance().tintColor = .white
    UIBarButtonItem.appearance().setTitleTextAttributes([
        NSAttributedString.Key.foregroundColor: .red,
        NSAttributedString.Key.font: UIFont(name: "font_name", size: 14)!
    ], for: .normal)

    return true
}

}
Scoria answered 23/10, 2020 at 16:21 Comment(0)
S
1

Swift 4.2 Add this functions ViewController

func addNavigationBarButton(imageName:String,direction:direction){
    var image = UIImage(named: imageName)
    image = image?.withRenderingMode(.alwaysOriginal)
    switch direction {
    case .left:
       self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: image, style:.plain, target: nil, action: #selector(goBack))
    case .right:
       self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: image, style:.plain, target: nil, action: #selector(goBack))
    }
}

@objc func goBack() {
    navigationController?.popViewController(animated: true)
}

enum direction {
    case right
    case left
}

Using you should use here

viewDidLoad()

addNavigationBarButton(imageName: "ic_back", direction:.left)
Safford answered 27/2, 2019 at 7:51 Comment(0)
W
1
let rectInsets = UIEdgeInsets(top: 0, left: -1, bottom: 0, right: 0)
let backButton = Asset.Assets.backArrow.image.withAlignmentRectInsets(rectInsets)
    
let appearance = UINavigationBarAppearance()
// if you need to hide the back button text
appearance.backButtonAppearance.normal.titleTextAttributes = [.foregroundColor: UIColor.clear]
// set custom back button image
appearance.setBackIndicatorImage(backButton, transitionMaskImage: backButton)
UINavigationBar.appearance().standardAppearance = appearance
Whin answered 27/5, 2023 at 0:40 Comment(0)
A
1

Swift 5 - UIKit

The other answers are great if you want a monochrome image. However, if you need to use a custom image with multiple colors use the following approach to create a custom button to add as a navigationItem.

In viewWillAppear:

// Create the custom back arrow button to use for back navigation.
// Done this way so it shows up as multi-color. Other ways more commonly found are monochrome only.
let backButton = UIButton(type: .custom)
backButton.setImage(UIImage(named: "your_image_name"), for: .normal)
backButton.addTarget(self, action: #selector(backButtonTapped), for: .touchUpInside)
backButton.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
    
// Moves the custom backButton slightly to the left so it's in the same spot where back navigation typically is.
var configuration = UIButton.Configuration.filled()
configuration.baseBackgroundColor = .clear
configuration.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: -20, bottom: 0, trailing: 0)
backButton.configuration = configuration
    
let backView = UIView(frame: CGRect(x: 0, y: 0, width: 70, height: 70))
backView.addSubview(backButton)
    
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backView)

Separately:

// Used to handle the custom back navigation button.
@objc func backButtonTapped() {
    self.navigationController?.popViewController(animated: true)
}
Alphonsoalphonsus answered 16/6, 2024 at 23:54 Comment(0)
N
0

Changing the navigation controller`s standardAppearance or scrollEdgeAppearance will reset your custom backIndicatorImage and backIndicatorTransitionMaskImage

let backImage = UIImage(systemName: "chevron.left.circle.fill")
navigationController?.navigationBar.backIndicatorImage = backImage
navigationController?.navigationBar.backIndicatorTransitionMaskImage = backImage

// this will cause the backIndicatorImage to be reset
let standardAppearance = UINavigationBarAppearance()
navigationController?.standardAppearance = standardAppearance

To change the backIndicatorImage in conjunction with UINavigationBarAppearance you will need to set the backImage using this:

navigationController?.standardAppearance.setBackIndicatorImage(backImage, transitionMaskImage: backImage)
Novelistic answered 23/2, 2023 at 14:41 Comment(0)
A
-1

You can change it globally in the AppDelegate with the following code:

UINavigationBar.appearance().backIndicatorImage = UIImage(named: "custom-back")
UINavigationBar.appearance().backIndicatorTransitionMaskImage = UIImage(named: "custom-back")
Askja answered 24/10, 2021 at 20:3 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.