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.

Michael Wasserfall
quelle

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.

Bryan Henry
quelle
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)
}
RTasche
quelle
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 { 
    }
}
Shyam Bhat
quelle
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.)

dk.
quelle
16

Swift 4

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

In Swift:

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

    super.viewWillDisappear(animated)

}
user754905
quelle
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)")
    }
}
normannisch
quelle
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
}
user1396236
quelle
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");
    }
 }
Byte
quelle
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
    }
}
Kyle Clegg
quelle
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")
            }
        }

    }
dengST30
quelle
-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.

Aaron
quelle
-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
bbrame
quelle