Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 486 Vote(s) - 3.58 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Detecting when the 'back' button is pressed on a navbar

#11
For Swift with a UINavigationController:

override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if self.navigationController?.topViewController != self {
print("back button tapped")
}
}
Reply

#12
This works for me in iOS 9.3.x with Swift:

override func didMoveToParentViewController(parent: UIViewController?) {
super.didMoveToParentViewController(parent)

if parent == self.navigationController?.parentViewController {
print("Back tapped")
}
}

Unlike other solutions here, this doesn't seem to trigger unexpectedly.
Reply

#13
I have solved this problem by adding a UIControl to the navigationBar on the left side .


UIControl *leftBarItemControl = [[UIControl alloc] initWithFrame:CGRectMake(0, 0, 90, 44)];
[leftBarItemControl addTarget:self action:@selector(onLeftItemClick:) forControlEvents:UIControlEventTouchUpInside];
self.leftItemControl = leftBarItemControl;
[self.navigationController.navigationBar addSubview:leftBarItemControl];
[self.navigationController.navigationBar bringSubviewToFront:leftBarItemControl];
And you need to remember to remove it when view will disappear:

- (void) viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if (self.leftItemControl) {
[self.leftItemControl removeFromSuperview];
}
}

That's all!

Reply

#14
[7ynk3r][1]'s answer was really close to what I did use in the end but it needed some tweaks:

- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {

UIViewController *topViewController = self.topViewController;
BOOL wasBackButtonClicked = topViewController.navigationItem == item;

if (wasBackButtonClicked) {
if ([topViewController respondsToSelector:@selector(navBackButtonPressed)]) {
// if user did press back on the view controller where you handle the navBackButtonPressed
[topViewController performSelector:@selector(navBackButtonPressed)];
return NO;
} else {
// if user did press back but you are not on the view controller that can handle the navBackButtonPressed
[self popViewControllerAnimated:YES];
return YES;
}
} else {
// when you call popViewController programmatically you do not want to pop it twice
return YES;
}
}


[1]:

[To see links please register here]

Reply

#15
**UPDATE:** According to some comments, the solution in the original answer does not seem to work under certain scenarios in iOS 8+. I can't verify that that is actually the case without further details.

For those of you however in that situation there's an alternative. Detecting when a view controller is being popped is possible by overriding `willMove(toParentViewController:)`. The basic idea is that a view controller is being popped when `parent` is `nil`.

Check out ["Implementing a Container View Controller"][1] for further details.

___

Since iOS 5 I've found that the easiest way of dealing with this situation is using the new method `- (BOOL)isMovingFromParentViewController`:

- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];

if (self.isMovingFromParentViewController) {
// Do your stuff here
}
}

`- (BOOL)isMovingFromParentViewController` makes sense when you are pushing and popping controllers in a navigation stack.

However, if you are presenting modal view controllers you should use `- (BOOL)isBeingDismissed` instead:

- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];

if (self.isBeingDismissed) {
// Do your stuff here
}
}

As noted in [this question](

[To see links please register here]

), you could combine both properties:

- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];

if (self.isMovingFromParentViewController || self.isBeingDismissed) {
// Do your stuff here
}
}

Other solutions rely on the existence of a `UINavigationBar`. Instead like my approach more because it decouples the required tasks to perform from the action that triggered the event, i.e. pressing a back button.


[1]:

[To see links please register here]

Reply

#16
You can use the back button callback, like this:

- (BOOL) navigationShouldPopOnBackButton
{
[self backAction];
return NO;
}

- (void) backAction {
// your code goes here
// show confirmation alert, for example
// ...
}

for swift version you can do something like in global scope

extension UIViewController {
@objc func navigationShouldPopOnBackButton() -> Bool {
return true
}
}

extension UINavigationController: UINavigationBarDelegate {
public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
return self.topViewController?.navigationShouldPopOnBackButton() ?? true
}
}

Below one you put in the viewcontroller where you want to control back button action:

override func navigationShouldPopOnBackButton() -> Bool {
self.backAction()//Your action you want to perform.

return true
}

Reply

#17
Those who claim that this doesn't work are mistaken:

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if self.isMovingFromParent {
print("we are being popped")
}
}

That works fine. So what is causing the widespread myth that it doesn’t?

The problem seems to be due to an incorrect implementation of a _different_ method, namely that the implementation of `willMove(toParent:)` forgot to call `super`.

If you implement `willMove(toParent:)` without calling `super`, then `self.isMovingFromParent` will be `false` and the use of `viewWillDisappear` will appear to fail. It didn't fail; you broke it.

**NOTE:** The real problem is usually the _second_ view controller detecting that the _first_ view controller was popped. Please see also the more general discussion here:

[To see links please register here]


**EDIT** A comment suggests that this should be `viewDidDisappear` rather than `viewWillDisappear`.
Reply

#18
I used [Pedro Magalhães][1] solution, except `navigationBar:shouldPop` was not called when I used it in an extension like this:

extension UINavigationController: UINavigationBarDelegate {
public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
return self.topViewController?.navigationShouldPopOnBackButton() ?? true
}

But the same thing in a `UINavigationController` subclass worked fine.

class NavigationController: UINavigationController, UINavigationBarDelegate {

func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
return self.topViewController?.navigationShouldPopOnBackButton() ?? true
}

I see some other questions reporting this method not being called (but the other delegate methods being called as expected), from iOS 13?

[To see links please register here]


[1]:

[To see links please register here]

Reply

#19
While `viewWillAppear()` and `viewDidDisappear()` *are* called when the back button is tapped, they are also called at other times. See end of answer for more on that.


## Using UIViewController.parent
Detecting the back button is better done when the VC is removed from its parent (the NavigationController) with the help of `willMoveToParentViewController(_:)` OR `didMoveToParentViewController()`

If parent is nil, the view controller is being popped off the navigation stack and dismissed. If parent is not nil, it is being added to the stack and presented.


// Objective-C
-(void)willMoveToParentViewController:(UIViewController *)parent {
[super willMoveToParentViewController:parent];
if (!parent){
// The back button was pressed or interactive gesture used
}
}


// Swift
override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent: parent)
if parent == nil {
// The back button was pressed or interactive gesture used
}
}

Swap out `willMove` for `didMove` and check self.parent to do work *after* the view controller is dismissed.

## Stopping the dismiss
Do note, checking the parent doesn't allow you to "pause" the transition if you need to do some sort of async save. To do that you could implement the following. Only downside here is you lose the fancy iOS styled/animated back button. Also be careful here with the interactive swipe gesture. Use the following to handle this case.

var backButton : UIBarButtonItem!

override func viewDidLoad() {
super.viewDidLoad()

// Disable the swipe to make sure you get your chance to save
self.navigationController?.interactivePopGestureRecognizer.enabled = false

// Replace the default back button
self.navigationItem.setHidesBackButton(true, animated: false)
self.backButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.Plain, target: self, action: "goBack")
self.navigationItem.leftBarButtonItem = backButton
}

// Then handle the button selection
func goBack() {
// Here we just remove the back button, you could also disabled it or better yet show an activityIndicator
self.navigationItem.leftBarButtonItem = nil
someData.saveInBackground { (success, error) -> Void in
if success {
self.navigationController?.popViewControllerAnimated(true)
// Don't forget to re-enable the interactive gesture
self.navigationController?.interactivePopGestureRecognizer.enabled = true
}
else {
self.navigationItem.leftBarButtonItem = self.backButton
// Handle the error
}
}
}




<br />
### More on view will/did appear
If you didn't get the `viewWillAppear` `viewDidDisappear` issue, Let's run through an example. Say you have three view controllers:

1. ListVC: _A table view of things_
2. DetailVC: _Details about a thing_
3. SettingsVC: _Some options for a thing_

Lets follow the calls on the `detailVC` as you go from the `listVC` to `settingsVC` and back to `listVC`

**List > Detail** (push detailVC) `Detail.viewDidAppear` <- appear
**Detail > Settings** (push settingsVC) `Detail.viewDidDisappear` <- disappear

_And as we go back..._
**Settings > Detail** (pop settingsVC) `Detail.viewDidAppear` <- appear
**Detail > List** (pop detailVC) `Detail.viewDidDisappear` <- disappear

Notice that `viewDidDisappear` is called multiple times, not only when going back, but also when going forward. For a quick operation that may be desired, but for a more complex operation like a network call to save, it may not.
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through