Versuch, die Navigationstastenaktion "Zurück" in iOS zu verarbeiten

76

Ich muss erkennen, wann der Benutzer auf die Schaltfläche "Zurück" in der Navigationsleiste tippt, um in diesem Fall einige Vorgänge auszuführen. Ich versuche, eine Aktion auf diese Schaltfläche manuell festzulegen:

[self.navigationItem.backBarButtonItem setAction:@selector(performBackNavigation:)];

- (void)performBackNavigation:(id)sender
{
   // Do operations

   [self.navigationController popViewControllerAnimated:NO];
}

Ich habe diesen Code zuerst im Ansichts-Controller selbst platziert, aber ich habe festgestellt, dass dies self.navigationItem.backBarButtonItemder Fall zu sein scheint nil. Deshalb habe ich denselben Code in den übergeordneten Ansichts-Controller verschoben, wodurch der erstere in den Navigationsstapel verschoben wird. Aber ich kann es auch nicht zum Laufen bringen. Ich habe einige Beiträge zu diesem Problem gelesen und einige sagten, dass der Selektor auf dem übergeordneten Ansichts-Controller eingestellt werden muss, aber für mich funktioniert es sowieso nicht ... Was könnte ich falsch machen?

Vielen Dank

AppsDev
quelle
Wäre es gut genug, den benötigten Code in viewWillDisappear zu platzieren?
DogCoffee
1
Verwenden Sie die Methoden auf UINavigationControllerDelegate.
Mike Weller
@ Smick Nein, leider wird das in meinem Szenario nicht ausreichen ...
AppsDev
@ MikeWeller Ich habe es versucht, aber ich konnte es nicht zum
Laufen bringen
Überprüfen Sie die Antwort in dieser Frage. Beste Lösung, die ich gefunden habe. stackoverflow.com/questions/1214965/…
James Parker

Antworten:

132

Versuchen Sie diesen Code mithilfe einer VIewWillDisappearMethode, um das Drücken der Zurück-Taste von NavigationItem zu erkennen:

-(void) viewWillDisappear:(BOOL)animated
{
    if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) 
    {
        // Navigation button was pressed. Do some stuff 
        [self.navigationController popViewControllerAnimated:NO];
    }
    [super viewWillDisappear:animated];
}

ODER Es gibt eine andere Möglichkeit, die Aktion der Navigationsrücktaste abzurufen.

Schaltfläche "Benutzerdefiniert" für UINavigationItem der Schaltfläche "Zurück" erstellen.

Zum Beispiel:

In ViewDidLoad:

- (void)viewDidLoad 
{
    [super viewDidLoad];
    UIBarButtonItem *newBackButton = [[UIBarButtonItem alloc] initWithTitle:@"Home" style:UIBarButtonItemStyleBordered target:self action:@selector(home:)];
    self.navigationItem.leftBarButtonItem=newBackButton;
}

-(void)home:(UIBarButtonItem *)sender 
{
    [self.navigationController popToRootViewControllerAnimated:YES];
}

Swift:

override func willMoveToParentViewController(parent: UIViewController?) 
{
    if parent == nil 
    {
        // Back btn Event handler
    }
}
Kumar KL
quelle
3
Ich glaube nicht, dass Sie [self.navigationController popViewControllerAnimated: NO] in viewWillDisappear benötigen.
Robert Kang
2
Ich denke, diese Methode funktioniert nicht mehr mit iOS 8, da self.navigationController.viewControllers nur ein Element enthält, == self
Sébastien Stormacq
1
@ Sébastien Stormacq Warum sagst du das? Es funktioniert in iOS 8.
21
1
Wenn Sie dies in ViewWillDisappear aufrufen, wird es nicht nur aufgerufen, wenn Sie auf die Schaltfläche Zurück tippen. Es wird immer dann aufgerufen, wenn der VC gepoppt wird oder wenn ein neuer VC gepusht wird
NSNoob
41

Schnell

override func didMoveToParentViewController(parent: UIViewController?) {
    if parent == nil {
        //"Back pressed"
    }
}
Dadachi
quelle
5
Das einzige Problem bei dieser Lösung ist, dass dies ausgelöst wird, wenn Sie streichen, um zurückzukehren und Ihre Meinung zu ändern.
Chris P
18

Vielleicht passt diese Antwort nicht zu Ihrer Erklärung, sondern zum Titel der Frage. Dies ist nützlich, wenn Sie wissen möchten, wann Sie die Zurück-Taste auf einem gedrückt haben UINavigationBar.

In diesem Fall können Sie das UINavigationBarDelegateProtokoll verwenden und eine der folgenden Methoden implementieren:

- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item;
- (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item;

Wenn die didPopItemMethode aufgerufen wird, haben Sie entweder die Zurück-Taste gedrückt oder Sie haben sie verwendet[UINavigationBar popNavigationItemAnimated:] Methode , und die Navigationsleiste hat das Element eingeblendet.

Nun, wenn Sie wissen möchten, welche Aktion die ausgelöst hat didPopItem Methode hat, können Sie ein Flag verwenden.

Bei diesem Ansatz muss ich kein Schaltflächenelement in der linken Leiste mit einem Pfeilbild manuell hinzufügen, um es der iOS-Zurück-Schaltfläche ähnlich zu machen und mein benutzerdefiniertes Ziel / meine benutzerdefinierte Aktion festlegen zu können.


Sehen wir uns ein Beispiel an:

Ich habe einen Ansichts-Controller mit einem Seitenansichts-Controller und eine benutzerdefinierte Seitenanzeige-Ansicht. Ich verwende auch eine benutzerdefinierte UINavigationBar, um einen Titel anzuzeigen, um zu wissen, auf welcher Seite ich bin, und die Schaltfläche "Zurück", um zur vorherigen Seite zurückzukehren. Außerdem kann ich auf dem Seiten-Controller zur vorherigen / nächsten Seite wischen.

#pragma mark - UIPageViewController Delegate Methods
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed {

    if( completed ) {

        //...

        if( currentIndex > lastIndex ) {

            UINavigationItem *navigationItem = [[UINavigationItem alloc] initWithTitle:@"Some page title"];

            [[_someViewController navigationBar] pushNavigationItem:navigationItem animated:YES];
            [[_someViewController pageControl] setCurrentPage:currentIndex];
        } else {
            _autoPop = YES; //We pop the item automatically from code.
            [[_someViewController navigationBar] popNavigationItemAnimated:YES];
            [[_someViewController pageControl] setCurrentPage:currentIndex];
        }
    }

}

Dann implementiere ich UINavigationBar-Delegatenmethoden:

#pragma mark - UINavigationBar Delegate Methods
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
    if( !_autoPop ) {
        //Pop by back button tap
    } else {
        //Pop from code
    }

    _autoPop = NO;

    return YES;
}

In diesem Fall habe ich verwendet, shouldPopItemweil der Pop animiert ist und ich die Zurück-Taste sofort bedienen und nicht warten wollte, bis der Übergang abgeschlossen ist.

Firula
quelle
12

Das Problem mit didMoveToParentViewController dabei ist, dass es aufgerufen wird, sobald die übergeordnete Ansicht wieder vollständig sichtbar ist. Wenn Sie also vorher einige Aufgaben ausführen müssen, funktioniert es nicht.

Und es funktioniert nicht mit der gesteuerten Animationsgeste. Verwenden willMoveToParentViewControllerfunktioniert besser.

Ziel c

- (void)willMoveToParentViewController:(UIViewController *)parent{
    if (parent == NULL) {
        // ...
    }
}

Schnell

override func willMoveToParentViewController(parent: UIViewController?) {
    if parent == nil {
        // ...  
    }
}
Nico
quelle
6

Dies ist die Objective-C-Version von Dadachis Antwort:

Ziel c

- (void)didMoveToParentViewController:(UIViewController *)parent{
    if (parent == NULL) {
        NSLog(@"Back Pressed");
    }
}
Ashish Kakkad
quelle
3

Legen Sie den Delegaten der UINavigationBar fest und verwenden Sie dann:

- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
    //handle the action here
}
Jeffrey Sun.
quelle
2
Wenn Sie UINavigationControllerdie Navigationsleiste mit a verwalten, führt der Versuch, den Delegaten festzulegen, zu einer Ausnahme: "*** Beenden der App aufgrund der nicht erfassten Ausnahme 'NSInternalInconsistencyException', Grund: 'Der Delegat kann nicht manuell in einer von einem Controller verwalteten UINavigationBar festgelegt werden. '". Das UINavigationControllerist der Delegierte. Dies bedeutet, dass Sie den Controller unterklassifizieren und die UINavigationBarDelegateMethoden überschreiben können (wahrscheinlich Super aufrufen).
Geoff Hackworth
Sie können Super UINavigationControllerjedoch nicht direkt aufrufen, da es nicht öffentlich konform ist UINavigationBarDelegate, was zu einem Compilerfehler führt! Möglicherweise gibt es eine Lösung mit UINavigationControllerDelegate.
Geoff Hackworth
3

Keine der anderen Lösungen hat bei mir funktioniert, aber das tut:

Erstellen Sie Ihre eigene Unterklasse von UINavigationController, lassen Sie UINavigationBarDelegate implementieren (der Delegat der Navigationsleiste muss nicht manuell festgelegt werden), fügen Sie eine UIViewController-Erweiterung hinzu, die eine Methode definiert, die beim Drücken einer Zurück-Taste aufgerufen werden soll, und implementieren Sie diese Methode dann in Ihrer UINavigationController-Unterklasse ::

func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
    self.topViewController?.methodToBeCalledOnBackButtonPress()
    self.popViewController(animated: true)
    return true
}
Schattig
quelle
Können Sie Ihre Antwort erweitern und zeigen, wie sie genau in einem View Controller verwendet wird?
Zeeshan
3

In Swift 4 oder höher:

override func didMove(toParent parent: UIViewController?) {
    if parent == nil {
        //"Back pressed"
    }
}
iMichele
quelle
2

Legen Sie das UINavigationControllerDelegate fest und implementieren Sie diese Delegatenfunktion (Swift):

func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
    if viewController is <target class> {
        //if the only way to get back - back button was pressed
    }
}
Kennzeichen
quelle
1

Verwenden Sie eine benutzerdefinierte UINavigationControllerUnterklasse, die das implementiertshouldPop Methode .

In Swift:

class NavigationController: UINavigationController, UINavigationBarDelegate
{
    var shouldPopHandler: (() -> Bool)?

    func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool
    {
        if let shouldPopHandler = self.shouldPopHandler, !shouldPopHandler()
        {
            return false
        }
        self.popViewController(animated: true) // Needed!
        return true
    }
}

Wenn eingestellt, wird Ihr shouldPopHandler() ist, werden Sie aufgerufen, um zu entscheiden, ob der Controller aktiviert sein soll oder nicht. Wenn es nicht eingestellt ist, wird es wie gewohnt geknallt.

Es ist eine gute Idee, UINavigationControllers zu deaktivieren, interactivePopGestureRecognizerda die Geste Ihren Handler sonst nicht aufruft.

Rivera
quelle