viewWillDisappear: Bestimmen Sie, ob der View-Controller geöffnet wird oder einen Sub-View-Controller anzeigt


134

Ich kämpfe darum, eine gute Lösung für dieses Problem zu finden. In der -viewWillDisappear:Methode eines Ansichtscontrollers muss ich einen Weg finden, um festzustellen, ob ein Ansichtscontroller auf den Stapel des Navigationscontrollers verschoben wird oder ob der Ansichtscontroller verschwindet, weil er gelöscht wurde.

Im Moment setze ich Flaggen wie, isShowingChildViewControlleraber es wird ziemlich kompliziert. Der einzige Weg, den ich zu erkennen glaube, ist die -deallocMethode.

Antworten:


228

Sie können Folgendes verwenden.

- (void)viewWillDisappear:(BOOL)animated {
  [super viewWillDisappear:animated];
  NSArray *viewControllers = self.navigationController.viewControllers;
  if (viewControllers.count > 1 && [viewControllers objectAtIndex:viewControllers.count-2] == self) {
    // View is disappearing because a new view controller was pushed onto the stack
    NSLog(@"New view controller was pushed");
  } else if ([viewControllers indexOfObject:self] == NSNotFound) {
    // View is disappearing because it was popped from the stack
    NSLog(@"View controller was popped");
  }
}

Dies ist natürlich möglich, da der View Controller-Stack des UINavigationController (verfügbar gemacht durch die viewControllers-Eigenschaft) zum Zeitpunkt des Aufrufs von viewWillDisappear aktualisiert wurde.


2
Perfekt! Ich weiß nicht, warum ich nicht daran gedacht habe! Ich glaube, ich hätte nicht gedacht, dass der Stapel geändert werden würde, bis die Methoden zum Verschwinden aufgerufen wurden! Danke :-)
Michael Waterfall

1
Ich habe nur versucht, das Gleiche zu tun, aber viewWillAppeares scheint, dass das viewControllers-Array in beide Richtungen gleich ist, unabhängig davon, ob der View-Controller durch Drücken oder etwas darüber angezeigt wird. Irgendwelche Ideen?
Michael Wasserfall

Ich sollte auch beachten, dass der View Controller viewDidLoadwährend der gesamten Lebensdauer der App dauerhaft ist, sodass ich meine Aktionen nicht ausführen kann, da er nur einmal aufgerufen wird! Hmm, kniffliger!
Michael Wasserfall

4
@Sbrocket gibt es einen Grund , warum Sie nicht tun , ![viewControllers containsObject:self]statt [viewControllers indexOfObject:self] == NSNotFound? Stilwahl?
Zekel

24
Diese Antwort ist seit iOS 5 veraltet. Mit -isMovingFromParentViewControllerder unten genannten Methode können Sie testen, ob die Ansicht explizit geöffnet wird.
Grahamparks

136

Ich denke, der einfachste Weg ist:

 - (void)viewWillDisappear:(BOOL)animated
{
    if ([self isMovingFromParentViewController])
    {
        NSLog(@"View controller was popped");
    }
    else
    {
        NSLog(@"New view controller was pushed");
    }
    [super viewWillDisappear:animated];
}

Schnell:

override func viewWillDisappear(animated: Bool)
{
    if isMovingFromParent
    {
        print("View controller was popped")
    }
    else
    {
        print("New view controller was pushed")
    }
    super.viewWillDisappear(animated)
}

Ab iOS 5 ist dies die Antwort, vielleicht überprüfen Sie auch isBeingDismissed
d370urn3ur

4
Für iOS7 muss ich [self.navigationController.viewControllers indexOfObject: self] == NSNotFound erneut überprüfen, da das Hintergrundbild der App diesen Test ebenfalls besteht, self jedoch nicht aus dem Navigationsstapel entfernt.
Eric Chen

3
Apple hat einen dokumentierten Weg bereitgestellt, um dies zu tun - stackoverflow.com/a/33478133/385708
Shyam Bhat

Das Problem bei der Verwendung von viewWillDisappear besteht darin, dass der Controller möglicherweise vom Stapel entfernt wird, während die Ansicht bereits verschwunden ist. Beispielsweise könnte ein anderer Viewcontroller auf den Stapel geschoben werden und dann popToRootViewControllerAnimated aufrufen, wobei viewWillDisappear für die mittleren umgangen wird.
John K

Angenommen, Sie haben zwei Controller (root vc und einen anderen Push) auf Ihrem Navigationsstapel. Wenn die dritte verschoben wird, wird viewWillDisappear für die zweite aufgerufen, deren Ansicht verschwinden wird, oder? Wenn Sie also zum Root-View-Controller wechseln (Popup der dritten und zweiten), wird viewWillDisappear auf der dritten, dh der letzten vc auf dem Stapel aufgerufen, da sich die Ansicht oben befindet und zu diesem Zeitpunkt verschwindet und die Ansicht der zweiten bereits verschwunden ist. Aus diesem Grund heißt diese Methode viewWillDisappear und nicht viewControllerWillBePopped.
RTasche

61

Aus Apples Dokumentation in UIViewController.h:

"Diese vier Methoden können in den Rückrufen zum Erscheinungsbild eines Ansichtscontrollers verwendet werden, um zu bestimmen, ob er als untergeordneter Ansichtscontroller angezeigt, verworfen oder hinzugefügt oder entfernt wird. Ein Ansichtscontroller kann beispielsweise überprüfen, ob er verschwindet, weil er entlassen wurde oder tauchte auf, indem er sich in seiner viewWillDisappear: -Methode fragte, indem er den Ausdruck überprüfte ([self isBeingDismissed] || [self isMovingFromParentViewController]). "

- (BOOL)isBeingPresented NS_AVAILABLE_IOS(5_0);

- (BOOL)isBeingDismissed NS_AVAILABLE_IOS(5_0);

- (BOOL)isMovingToParentViewController NS_AVAILABLE_IOS(5_0);

- (BOOL)isMovingFromParentViewController NS_AVAILABLE_IOS(5_0);

Ja, der einzige dokumentierte Weg, dies zu tun, ist der folgende:

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    if ([self isBeingDismissed] || [self isMovingFromParentViewController]) {
    }
}

Swift 3 Version:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    
    if self.isBeingDismissed || self.isMovingFromParentViewController { 
    }
}

18

Wenn Sie wollen einfach nur wissen , ob Ihre Ansicht geknallt wird immer, ich gerade entdeckt , dass self.navigationControllerist nilin viewDidDisappear, wenn es aus dem Stapel von Controllern entfernt wird. Das ist also ein einfacher alternativer Test.

(Dies stelle ich fest, nachdem ich alle möglichen anderen Verzerrungen ausprobiert habe. Ich bin überrascht, dass es kein Navigationscontroller-Protokoll gibt, mit dem ein Ansichtscontroller registriert werden kann, der bei Pops benachrichtigt werden soll. Sie können ihn nicht verwenden, UINavigationControllerDelegateda dies tatsächlich echte Anzeigearbeit ermöglicht.)


16

Swift 4

override func viewWillDisappear(_ animated: Bool)
    {
        super.viewWillDisappear(animated)
        if self.isMovingFromParent
        {
            //View Controller Popped
        }
        else
        {
            //New view controller pushed
        }
    }

6

In Swift:

 override func viewWillDisappear(animated: Bool) {
    if let navigationController = self.navigationController {
        if !contains(navigationController.viewControllers as! Array<UIViewController>, self) {
        }
    }

    super.viewWillDisappear(animated)

}

Stellen Sie sicher, dass Sie als verwenden! statt as
dfmuir

2

Ich finde Apples Dokumentation dazu schwer zu verstehen. Diese Erweiterung hilft dabei, die Zustände bei jeder Navigation anzuzeigen.

extension UIViewController {
    public func printTransitionStates() {
        print("isBeingPresented=\(isBeingPresented)")
        print("isBeingDismissed=\(isBeingDismissed)")
        print("isMovingToParentViewController=\(isMovingToParentViewController)")
        print("isMovingFromParentViewController=\(isMovingFromParentViewController)")
    }
}

1

Diese Frage ist ziemlich alt, aber ich habe sie zufällig gesehen, daher möchte ich Best Practice (afaik) veröffentlichen.

du kannst es einfach tun

if([self.navigationController.viewControllers indexOfObject:self]==NSNotFound)
 // view controller popped
}

1

Dies gilt für iOS7 , keine Ahnung, ob es für andere gilt. Soweit ich weiß, wurde in viewDidDisappearder Ansicht bereits geknallt. Das heißt, wenn Sie abfragen, erhalten self.navigationController.viewControllersSie eine nil. Überprüfen Sie also einfach, ob das Null ist.

TL; DR

 - (void)viewDidDisappear:(BOOL)animated
 {
    [super viewDidDisappear:animated];
    if (self.navigationController.viewControllers == nil) {
        // It has been popped!
        NSLog(@"Popped and Gone");
    }
 }

1

Segues können eine sehr effektive Möglichkeit sein, dieses Problem in iOS 6+ zu lösen. Wenn Sie dem bestimmten Abschnitt in Interface Builder eine Kennung gegeben haben, können Sie diese einchecken prepareForSegue.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"LoginSegue"]) {
       NSLog(@"Push");
       // Do something specific here, or set a BOOL indicating
       // a push has occurred that will be checked later
    }
}

1

Danke @Bryan Henry, arbeitet immer noch in Swift 5

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        if let controllers = navigationController?.children{
            if controllers.count > 1, controllers[controllers.count - 2] == self{
                // View is disappearing because a new view controller was pushed onto the stack
                print("New view controller was pushed")
            }
            else if controllers.firstIndex(of: self) == nil{
                // View is disappearing because it was popped from the stack
                print("View controller was popped")
            }
        }

    }

-1

Ich gehe davon aus, dass Sie meinen, dass Ihre Ansicht durch Drücken einer neuen Ansicht auf den Stapel des Navigationscontrollers verschoben wird, wenn Sie sagen, dass sie auf den Stapel verschoben wurde. Ich würde vorschlagen , die unter Verwendung von viewDidUnloadVerfahren zur Herstellung einer hinzuzufügen NSLogAnweisung zu schreiben , etwas an die Konsole , so dass Sie sehen können , was los ist, können Sie eine hinzufügen möchten NSLogzu viewWillDissappeer.


-1

Hier ist eine Kategorie, um das Gleiche wie die Antwort von sbrocket zu erreichen:

Header:

#import <UIKit/UIKit.h>

@interface UIViewController (isBeingPopped)

- (BOOL) isBeingPopped;

@end

Quelle:

#import "UIViewController+isBeingPopped.h"

@implementation UIViewController (isBeingPopped)

- (BOOL) isBeingPopped {
    NSArray *viewControllers = self.navigationController.viewControllers;
    if (viewControllers.count > 1 && [viewControllers objectAtIndex:viewControllers.count-2] == self) {
        return NO;
    } else if ([viewControllers indexOfObject:self] == NSNotFound) {
        return YES;
    }
    return NO;
}

@end
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.