3

How do I set a custom image to all back buttons of view controllers pushed in a UINavigationController?

My issues are:

  • must look like leftBarButtonItem, position-wise (because the backBarButtonItem itself is too glued to the left and I can't seem to change it's horizontal alignment).
  • has to be on all back actions (instead of manually setting on each view controller).
  • having a method setCustomBackButton and calling it on each view controller is also not an option, I'm looking for something like UINavigationBar.appearance(), i.e., throughout the app.

Something like this: enter image description here But with the back action working without me manually setting the selector on each view controller.

UPDATE: In response to Joe's solution, I'm getting that error: enter image description here

Rodrigo Ruiz
  • 4,248
  • 6
  • 43
  • 75
  • check this post https://stackoverflow.com/questions/43546132/how-to-customize-the-navigation-back-symbol-and-navigation-back-text/43556837#43556837 – Joe Jun 06 '17 at 23:57
  • @Joe that's exactly what I wanted to avoid, setting it on every view controller. – Rodrigo Ruiz Jun 07 '17 at 15:07

4 Answers4

4
UINavigationBar.appearance().backIndicatorImage = UIImage(named: "backArrow")

See Here: https://www.raywenderlich.com/108766/uiappearance-tutorial

crizzis
  • 9,978
  • 2
  • 28
  • 47
  • Tried it, the text from the previous view controller will still be there. – Rodrigo Ruiz Jun 06 '17 at 21:47
  • Actually I just tried again and it doesn't even change anything. – Rodrigo Ruiz Jun 06 '17 at 21:51
  • You didn't mention you also wanted to hide the back button title, which is an entirely different matter. See here: https://stackoverflow.com/questions/19078995/removing-the-title-text-of-an-ios-uibarbuttonitem – crizzis Jun 06 '17 at 21:55
  • Also, where are you setting `backIndicatorImage`? I'd say `application:didFinishLaunchingWithOptions:` would be the best option to make sure it gets applied to every navigation bar – crizzis Jun 06 '17 at 22:00
  • Well, I did upload an image to show what I wanted =), but still, I also tried that hack and it doesn't work, the title from the current view controller is pushed to the right, because the title from the previous controller is still there (even though it's pushed to the left). – Rodrigo Ruiz Jun 06 '17 at 23:41
  • We cannot see if the previous view controller had a title or not :P In any case, would the following work for you: 'If you use Storyboard, you can set navigation attributes inspector Back Button with space'? Make sure you use a space and not leave the 'back button' field empty, otherwise it will default to 'Back'. Unfortunately, you would need to do that for each view controller (note that you need to d&d a `Navigation Item` into a view controller scene first). – crizzis Jun 07 '17 at 09:35
  • Yeah, that's exactly what I wanted to avoid, doing it for every view controller. – Rodrigo Ruiz Jun 07 '17 at 15:07
1

Below answer based on the following OP answers:

Custom Back Button With Image and How to remove all navigationbar back button title

Try below code in didFinishLaunchingWithOptions method in AppDelegate.

To setting up a custom back button:

    let backArrowImage  = UIImage(named: "back") // set your back button image here
    let renderedImage = backArrowImage?.withRenderingMode(.alwaysOriginal)
    UINavigationBar.appearance().backIndicatorImage = renderedImage
    UINavigationBar.appearance().backIndicatorTransitionMaskImage = renderedImage

To hide a back button title:

    let barAppearace = UIBarButtonItem.appearance()
    barAppearace.setBackButtonTitlePositionAdjustment(UIOffsetMake(0, -60), for:UIBarMetrics.default)

Output: Updated

enter image description here

Update:

You need to add the following code to your More Information viewController to keep the title position.

override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
}
Joe
  • 8,868
  • 8
  • 37
  • 59
  • This is very close to what I need, but I still need to move the image a little to the right, just like on the `leftBarButtonItem`, which is the first bullet point of my question. – Rodrigo Ruiz Jun 12 '17 at 20:56
  • Actually in one case I still get the title alignment error. I'll add the image to the question – Rodrigo Ruiz Jun 12 '17 at 21:16
  • And in fact, it seems to happen when the title text is not big enough. – Rodrigo Ruiz Jun 12 '17 at 21:25
  • can't replicate the error. its works ok on iPhone 5 sim.even it works, if the mainVC title longer than normal in length or shorter. – Joe Jun 12 '17 at 21:36
  • Try setting the title of the previous view controller to be big as in "More information" and run on iPhone 5 simulator. So instead of "Main VC" write "More information". – Rodrigo Ruiz Jun 13 '17 at 02:34
  • That updated code forces me to set it up on every view controller, which is exactly what I wanted to avoid. – Rodrigo Ruiz Jun 13 '17 at 13:34
1

You can create your own subclass of UINavigationController and change the button inside the navigationController(_:willShow:animated:) delegate method as follows:

class MyNavigationController: UINavigationController, UINavigationControllerDelegate, UIGestureRecognizerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        delegate = self
        interactivePopGestureRecognizer?.delegate = self
    }

    func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
        if viewController != self.viewControllers.first { // don't add button to rootViewController
            let backButton = UIBarButtonItem(image: UIImage(named: "backArrow"), style: .plain, target: self, action: #selector(popViewController(animated:)) )
            viewController.navigationItem.leftBarButtonItem = backButton
        }
    }

}

Theoretically the above delegate method could live anywhere, but this way its logical and easy to select where you want to have this functionality.

Also don't forget to set the interactivePopGestureRecognizer delegate for not loosing the edge swipe gesture to go back (this somehow breaks when setting a new leftBarButtonItem).

The above method could be further improved by keeping track of which view controllers were already shown and then only replace the leftBarButtonItem on new ones (right now it also replaces it when going back/popping to an already shown view controller).

bbjay
  • 1,728
  • 3
  • 19
  • 35
0

Try this Swift 4.2

extension YouFirstViewController: UINavigationControllerDelegate {

    func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
        if !(viewController is YouFirstViewController) {
            let backButton = UIBarButtonItem(image: UIImage(named: "icnBack"), style: .plain, target: self, action: #selector(popview))
            viewController.navigationItem.leftBarButtonItem = backButton
        }
    }

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

}

onYouFirstViewController

class YouFirstViewController: UIViewcontroller {

    override func viewDidLoad() {
        self.navigationController?.delegate = self
    }
}