Problem mit der iPhone-UINavigation - Verschachtelte Push-Animationen können zu einer beschädigten Navigationsleiste führen

68

Ich erhalte immer wieder folgende Fehler:

2011-04-02 14:55:23.350 AppName[42430:207] nested push animation can result in corrupted navigation bar
2011-04-02 14:55:23.352 AppName[42430:207] nested push animation can result in corrupted navigation bar
2011-04-02 14:55:23.729 AppName[42430:207] Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.
2011-04-02 14:55:23.729 AppName[42430:207] Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.

Hier ist was ich tue. Von einem View Controller aus rufe ich Folgendes auf, wenn eine bestimmte Taste gedrückt wird:

EventsViewController *viewController = [[EventsViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController];
navController.navigationBar.tintColor = [UIColor blackColor];
[self presentModalViewController:navController animated:YES];
[viewController release];
[navController release];

Wenn dann eine bestimmte Taste in EventsController gedrückt wird, rufe ich auf:

SingleEventViewController *viewController = [[SingleEventViewController alloc] initWithEvent:[currentEvents objectAtIndex:indexPath.row]];
[self.navigationController pushViewController:viewController animated:YES];
[viewController release];

Wenn dann in SingleEventViewController eine bestimmte Taste gedrückt wird, rufe ich auf:

EventMapView* viewController = [[EventMapView alloc] initWithCoordinates];
[[self navigationController] pushViewController:viewController animated:YES];
[viewController release];

Ja, es ist offensichtlich, dass es verschachtelte Push-Animationen gibt, aber ist dies nicht der richtige Weg, dies zu tun? Ich habe den DrillDownSave-Code von Apple ausgecheckt, und so scheinen sie es zu machen. Ist es wichtig, dass ich init-Methoden anstelle von viewDidLoad-Methoden verwende?

Joshholat
quelle

Antworten:

118

Vorheriges pushViewControllerAnrufen viewDidAppearist unsicher.

SB
quelle
1
Auf jeden Fall ist dies die Erklärung für das Problem
daniherculano
2
Tritt auch auf, wenn Sie pushViewController im sekundären Thread aufrufen.
Ben Artin
1
Ich sehe, wie das der Fall wäre. Können Sie jedoch eine Dokumentation bereitstellen, die darüber spricht? Es ist nicht in den UINavigationController-Dokumenten
Pietro Rea
@PietroRea Ich glaube nicht, dass eine solche Behauptung wörtlich im Dokument zu finden ist. Das Dokument sagt jedoch, dass pushViewController "normalerweise als Reaktion darauf verwendet wird, dass der Benutzer ein Element auswählt", dh nachdem die aktuelle Ansicht verfügbar ist. Es ist daher logisch zu erwarten, dass eine Neuordnung dieser Ereignisse den internen FSM verwirren kann.
SB
Wirklich danke. Sie haben mir Zeit gespart, ich benutze das Enthüllungsmenü und wenn ich den Viewcontroller auf View drücke, erscheint alles falsch. Vielen Dank wirklich :)
Erhan Demirci
92

ZWEIMAL UNBEABSICHTIGT DIE GLEICHE SEGUE AUSLÖSEN Einmal im Code und einmal vom Interface Builder, aber beide gleichzeitig ...

Ich habe den gleichen Fehler bekommen wie der Rest von euch. Das einzige Problem war, dass ich versehentlich zweimal denselben Abschnitt abgefeuert habe. Einmal vom Interface Builder und einmal aus meinem Code.

Ich habe eine UITableView. Wenn eine Zelle ausgewählt ist, wird ein Abschnitt im Interface Builder ausgelöst. Hier ist mein Problem, ich hatte den Segue so eingerichtet, dass er direkt ausgelöst wird, indem ich auf CELL ITSELf im Interface Builder klicke. Dann hatte ich in meinem Code unter didSelectRowAtIndexPath einen Code, der denselben Segue auslösen würde ... wie so ...

[self performSegueWithIdentifier:@"MySegue" sender:tableView];

Das heißt, wenn didSelectRowAtIndexPath aufgerufen wird, weil eine Zeile ausgewählt wurde, wird der Abschnitt mit der obigen Codezeile ausgelöst. Dann löst der Interface Builder auch den Übergang aus, da er direkt mit dem Zellenobjekt im Interface Builder verbunden ist. So verhindern Sie, dass der Interface Builder den Segue direkt auslöst. Sie müssen den Übergang von der Oberseite des Ansichts-Controllers aus verbinden, der nicht in der Zelle selbst verschachtelt ist.

Wenn Sie dieses Problem aus demselben Grund wie ich haben, dh, dass Sie zweimal denselben Abschnitt aufrufen, können Sie dies beheben, indem Sie die Verbindung von der Zelle DIREKT zu Ihrem Abschnitt trennen und die Verbindung zwischen dem Abschnitt herstellen oben in der Tabellenhierarchie in IB, anstatt in der Zelle verschachtelt. Verbinden Sie den Segue von Ihrem View Controller selbst mit dem Segue. Wenn Sie dies richtig gemacht haben, sollte bei Auswahl des Segues die GESAMTE Ansicht hervorgehoben werden, aus der es stammt, nicht nur die Zelle.

Jetzt heißt Apples Dokumentation also unter performSegueWithIdentifier: sender: reference:

Apps müssen normalerweise keine Segues direkt auslösen. Stattdessen konfigurieren Sie im Interface Builder ein Objekt, das dem Ansichtscontroller zugeordnet ist, z. B. ein Steuerelement, das in seine Ansichtshierarchie eingebettet ist, um den Übergang auszulösen. Sie können diese Methode jedoch aufrufen, um einen Übergang programmgesteuert auszulösen, möglicherweise als Reaktion auf eine Aktion, die in der Storyboard-Ressourcendatei nicht angegeben werden kann. Sie können es beispielsweise von einem benutzerdefinierten Aktionshandler aus aufrufen, der zum Verarbeiten von Verwacklungs- oder Beschleunigungsmesserereignissen verwendet wird.

In meinem Fall habe ich eine Suchschaltfläche für meine UITableView, und es musste ermittelt werden, ob der Segue aufgerufen wird, wenn die Suchergebnistabelle vorhanden ist oder die normale Tabellenansicht vorhanden ist. Also musste ich den Übergang direkt auslösen.

Entfernen Sie also das eingebettete Steuerelement aus dem Interface Builder, kleben Sie es einfach auf den View Controller selbst und lösen Sie dann den Übergang in Ihrem Code aus!

Jetzt keine doppelten Segues mehr! Und keine Fehler mehr.

Ich hoffe, das hilft, ich habe ein paar Stunden gebraucht, um dieses Problem anzugehen.

user1927446
quelle
1
Mein Problem wurde auch durch zweimaliges Auslösen für einen Knopf verursacht
Lomec
11
Sie, Sir, sind ein Lebensretter.
Geekchic
1
Ich hatte einen Übergang für a UICellViewin IB eingerichtet und dann Code implementiert, um eine neue VC zu erstellen self.storyboardund meine Daten an diese VC weiterzuleiten und dann durchzuschieben [self.navigationController pushViewController:vc animated:YES];. Meine Lösung bestand darin, den Übergang in IB zu entfernen.
Saad Masood
Ich benutze kein Storyboard. Ich benutze Pushviewcontroller. Wie kann dieses Problem verhindert werden?
Gajendra K Chauhan
13

Ich hatte das gleiche Problem / die gleiche Fehlermeldung wie Sie gerade, suchte nach einer Lösung und landete in diesem Thread. Für mich stellte ich jedoch fest, dass für die Lösung tatsächlich nur eine animiert ist: JA, wenn ein verschachtelter Push ausgeführt wird (I. animiert setzen: JA nur für den letzten Push), hoffe das hilft

Prost.

code007
quelle
Ihr Kommentar hat es mir ermöglicht, das Rätsel zu lösen - siehe meinen Kommentar zur akzeptierten Antwort unten, er kann Ihnen auch in Zukunft helfen.
Charles Randall
Ich habe keine Fehlermeldung erhalten, aber in der Navigationsleiste meiner ersten Ansicht wurde die Navigationsleiste der mittleren Ansicht angezeigt, nachdem die dritte Ansicht verlassen wurde. Das Problem wurde behoben, indem der Pop nicht von oben animiert wurde.
Bill
Danke, das macht so viel Sinn, wenn Sie es erwähnen. Ich hatte den anfänglichen Controller mit animiert: JA und dann animiert: NEIN gedrückt, um den Benutzer an die richtige Stelle zu bringen.
Kalle
9

Ich habe es herausgefunden. Wenn Sie -pushViewController von außerhalb der -didSelectRowAtIndexPath-Methode eines UITableViewDelegate aufrufen, funktioniert dies anscheinend nicht. Das Verschieben des Aufrufs in diese Funktion hat funktioniert. Seltsam.

Joshholat
quelle
20
Dies löst das Problem, da didSelectRowAtIndexPath erst aufgerufen werden kann, nachdem die Animation der Ansicht abgeschlossen wurde. Wenn Sie einfach sicherstellen, dass Sie Ihren pushViewController in viewDidAppear aufrufen: oder höher, beschwert sich iOS nicht über verschachtelte pushViewController-Aufrufe.
Charles Randall
Ich verstehe nicht wirklich "Anscheinend funktioniert es nicht, wenn Sie -pushViewController von außerhalb der -didSelectRowAtIndexPath-Methode eines UITableViewDelegate aufrufen.". Es funktioniert in meinem Projekt.
Henson Fang
8

Ich bin auf dasselbe Problem gestoßen, das daraus resultierte, dass ein Knopf in einer Feder mit zwei verschiedenen Aktionen verbunden war. Es wurde versucht, beide Ansichts-Controller zu laden, wodurch der Stapel beschädigt wurde.

TigerCoding
quelle
3
Ich hatte auch ein ähnliches Problem. In meinem Fall habe ich eine Mischung aus Storyboard-Abschnitten und programmatischen Abschnitten (performSegueWithIdentifier). Ich habe diesen Fehler erhalten, als ich versehentlich beide gleichzeitig ausgeführt habe.
Symmetric
2
Das war auch mein Problem. Ich hatte statische Tabellenzellen, die zum nächsten Controller "pushen". Ich hatte auch einen performSegueWithIdentifier in didSelectRowAtIndexPath. beide wurden angerufen, was zu dieser Nachricht führte.
Ekawas
Meins war fast genau das Problem, wie in dieser Antwort beschrieben, nur in IB hatte ich irgendwie zwei identische Aktionen für den gleichen Knopf. Ich bin überzeugt, dass dies zustande gekommen ist, weil ich das Kopieren und Einfügen in IB verwendet habe. Achtung.
Alyoshak
4

Was meinen Sie, wenn Sie sagen, dass Sie init-Methoden anstelle von viewDidLoad-Methoden verwenden?

Wenn Sie einen neuen Ansichts-Controller drücken, bevor der alte Push eine schlechte Chance hat, ausgeführt zu werden, wird diese Art von Fehler angezeigt. Wenn Sie also bestimmten Code in init einfügen und Dinge vorzeitig ausführen, kann dies sicherlich dazu führen, dass der Fehler gemeldet wird.

Zu dem Zeitpunkt, an dem init auf einem View Controller ausgeführt wird, wurde die View noch nicht geladen!

Occulus
quelle
1
Mein einziger Grund zu sagen, dass ich init über viewDidLoad oder loadView verwende, ist, dass Apples Beispiel-App die init-Methoden nicht zum Einrichten der Ansichten verwendet, sodass ich dachte, dass dies möglicherweise ein Teil davon sein könnte. Auch nachdem ich den gesamten Code in der init-Methode auskommentiert habe, tritt dieses Problem immer noch auf. Warum würde es nicht auch für SingleEventViewController passieren, wenn dies der Fall wäre?
Joshholat
Sie verwenden init nicht zum Einrichten Ihrer UI- / Ansichtselemente - dies ist nicht der übliche Ort. Schauen Sie sich die UIViewController-Dokumentation genau an. Eine Ansicht kann von einer Spitze geladen werden. Oder Sie können dies programmgesteuert in loadView tun. Oder Sie können eine Basisansicht über eine Schreibfeder einrichten und dann programmgesteuert weitere Elemente in viewDidLoad hinzufügen. Aber du solltest so etwas nicht in init machen.
Occulus
Ich mache es programmatisch. Sollte ich also den Speicher zuweisen und alle Eigenschaften in loadView festlegen und dieses Zeug dann tatsächlich zur Ansicht in der Init hinzufügen? Denn wenn ich versuche, wie [self.view addSubview: myTableView] zu tun; In der loadView-Methode stürzt es ab.
Joshholat
Zur Hölle, ja, wenn wir einen Perform Segue nur innerhalb der ViewDidLoad-Methode ausführen, werden die oben genannten Fehler angezeigt, und nicht nur, dass in den nächsten ViewControllern dort contentViews beschädigt sind, sondern auch dieselben älteren From View Controller-Inhalte. Also habe ich den performSegue-Methodenaufruf aus ViewDid Load entfernt und ihn auf eine Button-Aktion gesetzt und das Problem behoben. Und ich hatte versucht, diesen Methodenaufruf in der ViewDid Load-Methode zu verschwenden, nur zu Testzwecken! Weil ich das bald nach dem Laden der Ansicht tun musste. Also habe ich ihn von der ViewDidLoad getrennt.
Randika Vishman
2

Ähm, ich hatte dieses Problem und bin neu in der gesamten iOS-Entwicklerszene. Nachdem ich mir meinen Verbindungsinspektor (mit dem Eigentümer der Datei) im Interface Builder angesehen hatte, stellte ich fest, dass beim Kopieren einer Schaltfläche die vorherige Schaltflächenmethode sowie die neue Methode, die ich erstellt hatte, zugewiesen wurden. Ich denke, hier kam der verschachtelte Aspekt meines Problems her, da zwei verschiedene Methoden ausgeführt wurden, die beide einen Blick auf den Nav-Controller richteten. Ich weiß, dass dies bereits beantwortet wurde, aber ich dachte, ich würde es aufstellen, falls jemand anderes einen dummen Fehler wie meinen hatte.

N1234
quelle
1

Dies wurde bereits beantwortet, aber ich dachte, dies könnte anderen helfen, da ich den gleichen Fehler erhielt, jedoch ohne Tabellenansichten zu verwenden. Ich habe endlich das Problem herausgefunden.

Ich hatte eine vorhandene Schaltfläche, deren IBAction einen pushViewController aufrief. Ich hatte eine neue Schaltfläche erstellt, indem ich die vorhandene Schaltfläche kopierte. Die neue Schaltfläche hatte auch eine Aktion, die pushViewController aufrief. Als die neue Taste getippt wurde (innen nachbessern) und der View Controller gedrückt wurde, wurde dieser Fehler angezeigt. Ich habe die neue Schaltfläche gelöscht, sie von Grund auf neu erstellt, an die vorhandenen Ausgänge und Aktionen gebunden, und der Fehler wurde behoben.

Vito Andolini
quelle
1

Bin auf das gleiche Problem gestoßen. In meinem Fall fehlte mir eine Unterbrechung in der switch-Anweisung, sodass zwei Segues gleichzeitig abgefeuert wurden. Einfache Lösung für mich.

Salilathalye
quelle
1

Mein Problem hatte damit zu tun, dass die Tastatur aktiv war.

Dies wurde für mich verursacht, indem ein ViewController von der delegate-Methode eines textField verschoben wurde:

-(void)textFieldDidBeginEditing:(UITextField *)textField{

        FilterLocationViewController *destViewController = (FilterLocationViewController *)[self.storyboard instantiateViewControllerWithIdentifier:@"FilterLocationViewController"];

        [self.navigationController pushViewController:destViewController animated:YES];

}

Durch Ändern des Codes in diesen:

-(void)textFieldDidBeginEditing:(UITextField *)textField{

        [_textFieldLocation resignFirstResponder]; //adding this line

        FilterLocationViewController *destViewController = (FilterLocationViewController *)[self.storyboard instantiateViewControllerWithIdentifier:@"FilterLocationViewController"];

        [self.navigationController pushViewController:destViewController animated:YES];

}

(Hinzufügen der Zeile [textField resignFirstResponder];) Das Problem ging weg.

Grundsätzlich besteht die Lektion darin, dass Sie den Navigationscontroller-Stapel nicht ändern sollten, wenn die Tastatur ausgefallen ist.

stackOverFlew
quelle
1

Vor kurzem hatte ich das gleiche Problem. Der Grund war: -Ich habe versehentlich zweimal versucht, den View Controller zu öffnen. Sie können diesen Absturz überprüfen, indem Sie Haltepunkte auf Push- und Pop-View-Controllern festlegen

Baljeet Singh
quelle
0

1) Vielleicht können Sie versuchen, die erforderlichen Variablen als Eigenschaften zu übergeben, bevor Sie die Taste drücken UIViewController die init-Methoden mit Parametern verwenden. Höchstwahrscheinlich benötigen Sie diese Parameter ohnehin über Ihre init-Methode hinaus.

Außerdem initWithCoordinates:fehlen in Ihrer Methode die Parameter. Möglicherweise sind Ihre benutzerdefinierten Init-Methoden Teil des Problems.

2) Nur weil Sie es erwähnt haben viewDidLoad- diese Methode dient zur Initialisierung, nachdem eine Ansicht geladen wurde. Wenn Sie den UIViewController im Code erstellen, wie es scheint, sollten Sie loadViewIhre Unteransichten einrichten.

Mundi
quelle
Die initWithCoordinates-Methode akzeptiert noch keine Parameter. Ich hatte vor, sie hinzuzufügen, stieß dann aber zuerst auf dieses Problem und wurde beim Versuch, es zu debuggen, erwischt, bevor ich fortfuhr. Warum ist das dann aber auch bei SingleEventViewController nicht der Fall? Es werden Parameter am Init benötigt, die eingerichtet werden müssen. Und danke, ich werde es mit loadView versuchen.
Joshholat
0

Dies geschah für mich wegen meiner UIControlEvents

    [button addTarget:self action:@selector(callSecondView) forControlEvents:UIControlEventAllTouchEvents];

Ich hatte die Änderungen UIControlEventAllTouchEventsan UIControlEventTouchUpInsideoder aber möchten Sie Ihre Taste zu arbeiten , wenn Sie das Problem haben wegen eines UIButton Anrufs.

John Riselvato
quelle
0

Meine Lösung war

[self performSelector: @selector (moveTo) withObject: nil afterDelay: 0.5];

Ponja
quelle
0

Ich weiß nichts über andere. Ich denke, die meisten Leute, die StoryBoard verwenden, stehen vor einem solchen Problem. Ich benutze XIB.

In meinem Fall war das Problem, als ich mit Push zu einer anderen Ansicht wechselte, auch

 [self.navigationController popViewControllerAnimated:YES];

in der ViewWillDisappear der aktuellen Ansicht zur gleichen Zeit. Entfernen Sie es einfach und es funktioniert gut.

Ich habe POP wegen der Anforderung und des Flusses verwendet. Die Hierarchie war 1 -> 2 -> 3

Ich war in Ansicht 2 und wollte zu Ansicht 3 wechseln. In diesem Fall ist dieser Fehler aufgetreten.

Jasmeet
quelle
0

In meinem Fall habe ich sowohl den Push-Übergang vom Storyboard als auch programmgesteuert festgelegt. Hoffentlich hilft das jedem

Kreuzer
quelle
0

Ich hatte auch diese Fehlermeldung und die Übergänge in der Navigationsleiste und im Navigationscontroller waren seltsam. Mein Setup bestand aus einer Reihe von Navigations-Controllern, die in einen Controller in der Registerkartenleiste eingebettet waren. Das Problem war, dass ich super.viewDidLoad()meine Tab Bar Controller-Implementierung von nicht aufgerufen habeviewDidLoad .

Super aufrufen ist etwas, worauf die Dokumente deutlich hinweisen, dass Sie dies tun sollten, wenn Sie viewDidLoad überschreiben, und ich habe dies auf die harte Tour gelernt.

Vielleicht kann das auch jemand anderem helfen!

empee
quelle
0

Ich weiß, dass dies beantwortet wurde, aber es könnte anderen helfen.

Ich hatte das gleiche Problem, aber es wurde verursacht, weil ich ein schlechtes Ereignis für eine Info-Schaltfläche verwendet habe. Ich habe "UIControlEventAllTouchEvents" verwendet und dies erzeugte zwei Pushs derselben Ansicht in den Navigationscontroller. Das richtige Ereignis war "UIControlEventTouchUpInside". Ich bin neu in iOS.

Carlos B.
quelle
-1

Dies löst das Problem: https://github.com/nexuspod/SafeTransition

Wenn Sie einen Ansichts-Controller mit Animation (animiert: JA) drücken (oder platzen lassen), wird er nicht sofort abgeschlossen, und schlimme Dinge passieren, wenn Sie vor Abschluss der Animation einen weiteren Push oder Pop ausführen.

Versuchen Sie, zwei Ansichts-Controller gleichzeitig zu drücken oder zu öffnen, um diesen Fehler zu reproduzieren. Beispiel:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    UIViewController *vc = [[UIViewController alloc] init];
    [self.navigationController pushViewController:vc animated:YES];
}

Sie erhalten diesen Fehler:

2014-07-03 11: 54: 25.051 Demo [2840: 60b] Verschachtelte Push-Animation kann zu einer beschädigten Navigationsleiste führen. 2014-07-03 11: 54: 25.406 Demo [2840: 60b] Beenden eines Navigationsübergangs in einem unerwarteten Zustand . Der Unteransichtsbaum der Navigationsleiste ist möglicherweise beschädigt.

Fügen Sie einfach die Codedateien zu Ihrem Projekt hinzu und machen Sie Ihren Navigationscontroller zu einer Unterklasse von APBaseNavigationController.

Joywek
quelle
-1

Um die Liste zu vervollständigen, ist hier ein weiterer Grund, der dazu führen kann, dass "verschachtelte Push-Animationen zu einer beschädigten Navigationsleiste führen können":

Ich habe mehrere NavigationController in einem TabBarController eingerichtet und den ausgewählten Index in den Eigenschaften der Storyboard-Identifikation festgelegt. Nach dem Verschieben der aktiven Registerkarte zum Code verschwand der Fehler.

contmp
quelle