Rectangle 27 0

iphone Setting action for back button in navigation controller?


At least in Xcode 5, there is a simple and pretty good (not perfect) solution. In IB, drag a Bar Button Item off the Utilities pane and drop it on the left side of the Navigation Bar where the Back button would be. Set the label to "Back." You will have a functioning button that you can tie to your IBAction and close your viewController. I'm doing some work and then triggering an unwind segue and it works perfectly.

What isn't ideal is that this button does not get the < arrow and does not carry forward the previous VCs title, but I think this can be managed. For my purposes, I set the new Back button to be a "Done" button so it's purpose is clear.

You also end up with two Back buttons in the IB navigator, but it is easy enough to label it for clarity.

Note
Rectangle 27 0

iphone Setting action for back button in navigation controller?


At least in Xcode 5, there is a simple and pretty good (not perfect) solution. In IB, drag a Bar Button Item off the Utilities pane and drop it on the left side of the Navigation Bar where the Back button would be. Set the label to "Back." You will have a functioning button that you can tie to your IBAction and close your viewController. I'm doing some work and then triggering an unwind segue and it works perfectly.

What isn't ideal is that this button does not get the < arrow and does not carry forward the previous VCs title, but I think this can be managed. For my purposes, I set the new Back button to be a "Done" button so it's purpose is clear.

You also end up with two Back buttons in the IB navigator, but it is easy enough to label it for clarity.

Note
Rectangle 27 0

iphone Setting action for back button in navigation controller?


At least in Xcode 5, there is a simple and pretty good (not perfect) solution. In IB, drag a Bar Button Item off the Utilities pane and drop it on the left side of the Navigation Bar where the Back button would be. Set the label to "Back." You will have a functioning button that you can tie to your IBAction and close your viewController. I'm doing some work and then triggering an unwind segue and it works perfectly.

What isn't ideal is that this button does not get the < arrow and does not carry forward the previous VCs title, but I think this can be managed. For my purposes, I set the new Back button to be a "Done" button so it's purpose is clear.

You also end up with two Back buttons in the IB navigator, but it is easy enough to label it for clarity.

Note
Rectangle 27 0

iphone Setting action for back button in navigation controller?


At least in Xcode 5, there is a simple and pretty good (not perfect) solution. In IB, drag a Bar Button Item off the Utilities pane and drop it on the left side of the Navigation Bar where the Back button would be. Set the label to "Back." You will have a functioning button that you can tie to your IBAction and close your viewController. I'm doing some work and then triggering an unwind segue and it works perfectly.

What isn't ideal is that this button does not get the < arrow and does not carry forward the previous VCs title, but I think this can be managed. For my purposes, I set the new Back button to be a "Done" button so it's purpose is clear.

You also end up with two Back buttons in the IB navigator, but it is easy enough to label it for clarity.

Note
Rectangle 27 0

iphone Setting action for back button in navigation controller?


"If the name of a method declared in a category is the same as a method in the original class, or a method in another category on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime. This is less likely to be an issue if youre using categories with your own classes, but can cause problems when using categories to add methods to standard Cocoa or Cocoa Touch classes."

Note
Rectangle 27 0

iphone Setting action for back button in navigation controller?


override func viewWillDisappear(animated: Bool) {
    let viewControllers = self.navigationController?.viewControllers!
    if indexOfArray(viewControllers!, searchObject: self) == nil {
        // do something
    }
    super.viewWillDisappear(animated)
}

func indexOfArray(array:[AnyObject], searchObject: AnyObject)-> Int? {
    for (index, value) in enumerate(array) {
        if value as UIViewController == searchObject as UIViewController {
            return index
        }
    }
    return nil
}
Note
Rectangle 27 0

iphone Setting action for back button in navigation controller?


@objc public protocol BackButtonDelegate {
      @objc optional func navigationShouldPopOnBackButton() -> Bool 
}

extension UINavigationController: UINavigationBarDelegate  {

    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {

        if viewControllers.count < (navigationBar.items?.count)! {                
            return true
        }

        var shouldPop = true
        let vc = self.topViewController

        if vc.responds(to: #selector(vc.navigationShouldPopOnBackButton)) {
            shouldPop = vc.navigationShouldPopOnBackButton()
        }

        if shouldPop {
            DispatchQueue.main.async {
                self.popViewController(animated: true)
            }
        } else {
            for subView in navigationBar.subviews {
                if(0 < subView.alpha && subView.alpha < 1) {
                    UIView.animate(withDuration: 0.25, animations: {
                        subView.alpha = 1
                    })
                }
            }
        }

        return false
    }
}
class BaseVC: UIViewController, BackButtonDelegate {
    func navigationShouldPopOnBackButton() -> Bool {
        if ... {
            return true
        } else {
            return false
        }        
    }
}
func navigationShouldPopOnBackButton() -> Bool {
     let alert = UIAlertController(title: "Warning",
                                          message: "Do you want to quit?",
                                          preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { UIAlertAction in self.yes()}))
            alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { UIAlertAction in self.no()}))
            present(alert, animated: true, completion: nil)
      return false
}

func yes() {
     print("yes")
     DispatchQueue.main.async {
            _ = self.navigationController?.popViewController(animated: true)
        }
}

func no() {
    print("no")       
}

And then, in your view controller add the delegate function:

Here is Swift 3 version of @oneway's answer for catching navigation bar back button event before it gets fired. As UINavigationBarDelegate cannot be used for UIViewController, you need to create a delegate that will be triggered when navigationBar shouldPop is called.

I've realised that we often want to add an alert controller for users to decide whether they wanna go back. If so, you can always return false in navigationShouldPopOnBackButton() function and close your view controller by doing something like this:

Note
Rectangle 27 0

iphone Setting action for back button in navigation controller?


-(BOOL) navigationShouldPopOnBackButton {
    if(needsShowConfirmation) {
        // Show confirmation alert
        // ...
        return NO; // Ignore 'Back' button this time
    }
    return YES; // Process 'Back' button click and Pop view controler
}
UINavigationBarDelegate
UIViewController
navigationBar:shouldPopItem:
navigationShouldPopOnBackButton

I just implemented this (pretty cool BTW) in iOS 7.1 and noticed that after returning NO the back button stays in a disabled state (visually, because it still receives and reacts to touch events). I worked around it by adding an else statement to the shouldPop check and cycling through the navigation bar subviews, and setting the alpha value back to 1 if needed inside an animation block: gist.github.com/idevsoftware/9754057

This is the cleanest solution I've seen, better and simpler than using your own custom UIButton. Thanks!

Note
Rectangle 27 0

iphone Setting action for back button in navigation controller?


-(void) viewWillDisappear:(BOOL)animated {
    if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
       // back button was pressed.  We know this is true because self is no longer
       // in the navigation stack.  
    }
    [super viewWillDisappear:animated];
}
if (find(self.navigationController!.viewControllers as! [UIViewController],self)==nil)

+1 great hack, but does not offer control over animation of pop

Another problem is that you can't differentiate if the user pressed the back button or if you programatically called [self.navigationController popViewControllerAnimated:YES]

Doesn't work for me if I send a message to the delegate through a button and the delegate pops the controller - this still fires.

Try putting this into the view controller where you want to detect the press:

Note
Rectangle 27 0

iphone Setting action for back button in navigation controller?


extension MyViewController: NavigationControllerBackButtonDelegate {
    func shouldPopOnBackButtonPress() -> Bool {
        performSomeActionOnThePressOfABackButton()
        return false
    }
}
protocol NavigationControllerBackButtonDelegate {
    func shouldPopOnBackButtonPress() -> Bool
}

extension UINavigationController {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        // Prevents from a synchronization issue of popping too many navigation items
        // and not enough view controllers or viceversa from unusual tapping
        if viewControllers.count < navigationBar.items!.count {
            return true
        }

        // Check if we have a view controller that wants to respond to being popped
        var shouldPop = true
        if let viewController = topViewController as? NavigationControllerBackButtonDelegate {
            shouldPop = viewController.shouldPopOnBackButtonPress()
        }

        if (shouldPop) {
            DispatchQueue.main.async {
                self.popViewController(animated: true)
            }
        } else {
            // Prevent the back button from staying in an disabled state
            for view in navigationBar.subviews {
                if view.alpha < 1.0 {
                    UIView.animate(withDuration: 0.25, animations: {
                        view.alpha = 1.0
                    })
                }
            }

        }

        return false
    }
}

@FlorentBreton maybe a misunderstanding? shouldPopOnBackButtonPress should get called as long as there are no bugs. performSomeActionOnThePressOfABackButton is just a made-up method that does not exist.

@TimAutin I just tested this again and seems like something has changed. Key part to understand that in a UINavigationController, the navigationBar.delegate is set to the navigation controller. So the methods SHOULD get called. However, in Swift, I can't get them to be called, even in a subclass. I did, however, get them to be called in Objective-C, so I would just use the Objective-C version for now. Might be a Swift bug.

I understood it, that's why I created a method performSomeActionOnThePressOfABackButton in my controller to execute a specific action when the back button is pressed, but this method was never called, the action is a normal back return

In your view controller you just conform to a protocol and perform whatever action you need:

Maybe I missed something, but it does'nt work for me, the method performSomeActionOnThePressOfABackButton of the extension is never called

Not working for me neither. The shouldPop method is never called. Did you set a delegate somewhere?

Then create a class, say NavigationController+BackButton, and just copy-paste the code below:

Note