it-swarm.com.de

rücktaste Rückruf in navigationController in iOS

Ich habe einen Blick auf den Navigationscontroller geschoben und wenn ich die Zurück-Taste drücke, wird automatisch die vorherige Ansicht angezeigt. Ich möchte ein paar Dinge tun, wenn die Zurück-Taste gedrückt wird, bevor die Ansicht vom Stapel genommen wird. Welches ist die Rückruffunktion der Rücktaste?

96
Namratha

William Jockuschs Antwort Löse dieses Problem mit einfachen Tricks.

-(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];
}
159
ymutlu

Meiner Meinung nach die beste Lösung.

- (void)didMoveToParentViewController:(UIViewController *)parent
{
    if (![parent isEqual:self.parentViewController]) {
         NSLog(@"Back pressed");
    }
}

Aber es funktioniert nur mit iOS5 +

82
Blank

es ist wahrscheinlich besser, die Zurück-Schaltfläche zu überschreiben, damit Sie das Ereignis bearbeiten können, bevor die Ansicht für Dinge wie die Bestätigung des Benutzers geöffnet wird.

erstellen Sie in viewDidLoad ein UIBarButtonItem und legen Sie self.navigationItem.leftBarButtonItem fest, indem Sie ein sel übergeben

- (void) viewDidLoad
{
// change the back button to cancel and add an event handler
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:@”back”
style:UIBarButtonItemStyleBordered
target:self
action:@selector(handleBack:)];

self.navigationItem.leftBarButtonItem = backButton;
[backButton release];

}
- (void) handleBack:(id)sender
{
// pop to root view controller
[self.navigationController popToRootViewControllerAnimated:YES];

}

Dann können Sie beispielsweise UIAlertView aufrufen, um die Aktion zu bestätigen, und dann den View-Controller anzeigen, usw.

Anstatt eine neue Rücktaste zu erstellen, können Sie die UINavigationController-Delegatmethode verwenden, um Aktionen auszuführen, wenn die Zurück-Taste gedrückt wird.

25
roocell

Ich ende mit diesen Lösungen. Wenn wir auf die Schaltfläche "Zurück" tippen, wird die Methode viewDidDisappear aufgerufen. Wir können überprüfen, indem Sie den isMovingFromParentViewController-Selektor aufrufen, der true zurückgibt. Wir können Daten zurückgeben (Using Delegate).

-(void)viewDidDisappear:(BOOL)animated{

    if (self.isMovingToParentViewController) {

    }
    if (self.isMovingFromParentViewController) {
       //moving back
        //pass to viewCollection delegate and update UI
        [self.delegateObject passBackSavedData:self.dataModel];

    }
}
8
Avijit Nagare

Für "VOR dem Öffnen der Ansicht vom Stapel":

- (void)willMoveToParentViewController:(UIViewController *)parent{
    if (parent == nil){
        NSLog(@"do whatever you want here");
    }
}
7
Anum Malik

Dies ist der richtige Weg, dies zu erkennen. 

- (void)willMoveToParentViewController:(UIViewController *)parent{
    if (parent == nil){
        //do stuff

    }
}

diese Methode wird aufgerufen, wenn auch die Ansicht verschoben wird. Das Überprüfen von parent == nil dient dazu, den View-Controller vom Stack zu entfernen

6
Saad

Es gibt einen angemesseneren Weg, als die viewControllers zu fragen. Sie können aus Ihrem Controller einen Delegaten der Navigationsleiste mit der Zurück-Schaltfläche machen. Hier ist ein Beispiel. Weisen Sie in der Implementierung des Controllers, bei dem Sie das Drücken der Zurück-Taste ausführen möchten, an, dass das UINavigationBarDelegate-Protokoll implementiert wird:

@interface MyViewController () <UINavigationBarDelegate>

Dann irgendwo in Ihrem Initialisierungscode (wahrscheinlich in viewDidLoad) machen Sie Ihren Controller zum Delegaten seiner Navigationsleiste:

self.navigationController.navigationBar.delegate = self;

Implementieren Sie schließlich die shouldPopItem-Methode. Diese Methode wird direkt aufgerufen, wenn die Zurück-Taste gedrückt wird. Wenn Sie mehrere Controller oder Navigationselemente im Stack haben, möchten Sie wahrscheinlich prüfen, welches dieser Navigationselemente (der Parameter item) ausgepuffert wird, so dass Sie nur Ihre eigenen Aufgaben erledigen können, wenn Sie dies erwarten. Hier ist ein Beispiel:

-(BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
    NSLog(@"Back button got pressed!");
    //if you return NO, the back button press is cancelled
    return YES;
}
4
Carlos Guzman

Hier ist eine andere Möglichkeit, die ich implementiert habe (habe es nicht mit einem Abwicklungssegment getestet, aber es würde wahrscheinlich nicht differenzieren, wie andere in Bezug auf andere Lösungen auf dieser Seite angegeben haben), damit der übergeordnete Ansichtscontroller Aktionen vor dem untergeordneten Element ausführt VC it push wird aus dem Ansichtsstapel entfernt (ich habe dies ein paar Ebenen niedriger als der ursprüngliche UINavigationController verwendet.) Dies kann auch verwendet werden, um Aktionen auszuführen, bevor childVC gepusht wird. Dies hat den zusätzlichen Vorteil anstatt ein benutzerdefiniertes UIBarButtonItem oder UIButton zu erstellen.

  1. Lassen Sie Ihre Eltern VC das UINavigationControllerDelegate -Protokoll übernehmen und sich für delegierte Nachrichten registrieren:

    MyParentViewController : UIViewController <UINavigationControllerDelegate>
    
    -(void)viewDidLoad {
        self.navigationcontroller.delegate = self;
    }
    
  2. Implementieren Sie diese UINavigationControllerDelegate -Instanzmethode in MyParentViewController:

    - (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
        // Test if operation is a pop; can also test for a Push (i.e., do something before the ChildVC is pushed
        if (operation == UINavigationControllerOperationPop) {
            // Make sure it's the child class you're looking for
            if ([fromVC isKindOfClass:[ChildViewController class]]) {
                // Can handle logic here or send to another method; can also access all properties of child VC at this time
                return [self didPressBackButtonOnChildViewControllerVC:fromVC];
            }
        }
        // If you don't want to specify a nav controller transition
        return nil;
    }
    
  3. Wenn Sie eine bestimmte Rückruffunktion in der obigen Instanzmethode UINavigationControllerDelegate angeben

    -(id <UIViewControllerAnimatedTransitioning>)didPressBackButtonOnAddSearchRegionsVC:(UIViewController *)fromVC {
        ChildViewController *childVC = ChildViewController.new;
        childVC = (ChildViewController *)fromVC;
    
        // childVC.propertiesIWantToAccess go here
    
        // If you don't want to specify a nav controller transition
        return nil;
    

    }

3
Evan R

Wenn Sie "viewWillDisappear" oder eine ähnliche Methode nicht verwenden können, versuchen Sie, UINavigationController zu subclass. Dies ist die Kopfklasse:

#import <Foundation/Foundation.h>
@class MyViewController;

@interface CCNavigationController : UINavigationController

@property (nonatomic, strong) MyViewController *viewController;

@end

Implementierungsklasse:

#import "CCNavigationController.h"
#import "MyViewController.h"

@implementation CCNavigationController {

}
- (UIViewController *)popViewControllerAnimated:(BOOL)animated {
    @"This is the moment for you to do whatever you want"
    [self.viewController doCustomMethod];
    return [super popViewControllerAnimated:animated];
}

@end

Auf der anderen Seite müssen Sie diesen viewController mit Ihrem benutzerdefinierten NavigationController verknüpfen. Führen Sie in der viewDidLoad-Methode für Ihren regulären viewController Folgendes aus:

@implementation MyViewController {
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        ((CCNavigationController*)self.navigationController).viewController = self;
    }
}
3
George Harley

Vielleicht ist es ein bisschen zu spät, aber ich wollte das gleiche Verhalten auch schon einmal. Und die Lösung, mit der ich gearbeitet habe, funktioniert ganz gut in einer der Apps, die derzeit im App Store erhältlich sind. Da ich noch niemanden mit einer ähnlichen Methode gesehen habe, möchte ich sie hier teilen. Der Nachteil dieser Lösung ist, dass eine Unterklasse von UINavigationController erforderlich ist. Obwohl ich Method Swizzling verwende, um das zu vermeiden, bin ich nicht so weit gegangen.

Die Standard-Zurück-Schaltfläche wird also von UINavigationBar verwaltet. Wenn ein Benutzer auf die Schaltfläche Zurück tippt, fragt UINavigationBar seinen Stellvertreter, ob er die oberste UINavigationItem anzeigen soll, indem er navigationBar(_:shouldPop:) aufruft. UINavigationController implementiert dies tatsächlich, deklariert jedoch nicht öffentlich, dass UINavigationBarDelegate übernommen wird (warum !?). Um dieses Ereignis abzufangen, erstellen Sie eine Unterklasse von UINavigationController, deklarieren die Konformität mit UINavigationBarDelegate und implementieren navigationBar(_:shouldPop:). Geben Sie true zurück, wenn das oberste Element angezeigt werden soll. Geben Sie false zurück, wenn es bleiben soll.

Es gibt zwei Probleme. Das erste ist, dass Sie irgendwann die UINavigationController-Version von navigationBar(_:shouldPop:) aufrufen müssen. UINavigationBarController deklariert die Konformität mit UINavigationBarDelegate jedoch nicht öffentlich, und der Versuch, sie aufzurufen, führt zu einem Fehler bei der Kompilierung. Als Lösung habe ich Objective-C-Laufzeit verwendet, um die Implementierung direkt abzurufen und aufzurufen. Bitte lassen Sie mich wissen, wenn jemand eine bessere Lösung hat.

Das andere Problem ist, dass navigationBar(_:shouldPop:) zuerst aufgerufen wird, gefolgt von popViewController(animated:), wenn der Benutzer auf die Zurück-Schaltfläche tippt. Die Reihenfolge wird umgekehrt, wenn der View-Controller durch Aufrufen von popViewController(animated:) aufgerufen wird. In diesem Fall verwende ich einen Booleschen Wert, um zu erkennen, ob popViewController(animated:) vor navigationBar(_:shouldPop:) aufgerufen wird, was bedeutet, dass der Benutzer auf die Schaltfläche "Zurück" geklickt hat.

Außerdem erstelle ich eine Erweiterung von UIViewController, damit der Navigationscontroller den Ansichtscontroller fragt, ob er angezeigt werden soll, wenn der Benutzer auf die Schaltfläche "Zurück" tippt. View-Controller können false zurückgeben und alle erforderlichen Aktionen ausführen und popViewController(animated:) später aufrufen.

class InterceptableNavigationController: UINavigationController, UINavigationBarDelegate {
    // If a view controller is popped by tapping on the back button, `navigationBar(_:, shouldPop:)` is called first follows by `popViewController(animated:)`.
    // If it is popped by calling to `popViewController(animated:)`, the order reverses and we need this flag to check that.
    private var didCallPopViewController = false

    override func popViewController(animated: Bool) -> UIViewController? {
        didCallPopViewController = true
        return super.popViewController(animated: animated)
    }

    func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        // If this is a subsequence call after `popViewController(animated:)`, we should just pop the view controller right away.
        if didCallPopViewController {
            return originalImplementationOfNavigationBar(navigationBar, shouldPop: item)
        }

        // The following code is called only when the user taps on the back button.

        guard let vc = topViewController, item == vc.navigationItem else {
            return false
        }

        if vc.shouldBePopped(self) {
            return originalImplementationOfNavigationBar(navigationBar, shouldPop: item)
        } else {
            return false
        }
    }

    func navigationBar(_ navigationBar: UINavigationBar, didPop item: UINavigationItem) {
        didCallPopViewController = false
    }

    /// Since `UINavigationController` doesn't publicly declare its conformance to `UINavigationBarDelegate`,
    /// trying to called `navigationBar(_:shouldPop:)` will result in a compile error.
    /// So, we'll have to use Objective-C runtime to directly get super's implementation of `navigationBar(_:shouldPop:)` and call it.
    private func originalImplementationOfNavigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        let sel = #selector(UINavigationBarDelegate.navigationBar(_:shouldPop:))
        let imp = class_getMethodImplementation(class_getSuperclass(InterceptableNavigationController.self), sel)
        typealias ShouldPopFunction = @convention(c) (AnyObject, Selector, UINavigationBar, UINavigationItem) -> Bool
        let shouldPop = unsafeBitCast(imp, to: ShouldPopFunction.self)
        return shouldPop(self, sel, navigationBar, item)
    }
}

extension UIViewController {
    @objc func shouldBePopped(_ navigationController: UINavigationController) -> Bool {
        return true
    }
}

Implementieren Sie in Ihren View-Controllern shouldBePopped(_:). Wenn Sie diese Methode nicht implementieren, wird standardmäßig der Ansichts-Controller angezeigt, sobald der Benutzer wie gewohnt auf die Schaltfläche "Zurück" tippt.

class MyViewController: UIViewController {
    override func shouldBePopped(_ navigationController: UINavigationController) -> Bool {
        let alert = UIAlertController(title: "Do you want to go back?",
                                      message: "Do you really want to go back? Tap on \"Yes\" to go back. Tap on \"No\" to stay on this screen.",
                                      preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: nil))
        alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { _ in
            navigationController.popViewController(animated: true)
        }))
        present(alert, animated: true, completion: nil)
        return false
    }
}

Sie können meine Demo anschauen hier .

 enter image description here 

2
yusuke024

Dies funktioniert bei mir in Swift:

override func viewWillDisappear(_ animated: Bool) {
    if self.navigationController?.viewControllers.index(of: self) == nil {
        // back button pressed or back gesture performed
    }

    super.viewWillDisappear(animated)
}
1
pableiros

Wenn Sie ein Storyboard verwenden und von einem Push-Menü kommen, können Sie auch shouldPerformSegueWithIdentifier:sender: überschreiben.

0
Mojo66