Diese Linie:
[self dismissViewControllerAnimated:YES completion:nil];
sendet keine Nachricht an sich selbst, sondern sendet tatsächlich eine Nachricht an die präsentierende VC und fordert sie auf, die Entlassung vorzunehmen. Wenn Sie eine VC präsentieren, erstellen Sie eine Beziehung zwischen der präsentierenden und der präsentierten VC. Sie sollten die präsentierende VC also nicht zerstören, während sie präsentiert wird (die präsentierte VC kann diese Entlassungsnachricht nicht zurücksenden…). Da Sie dies nicht wirklich berücksichtigen, verlassen Sie die App in einem verwirrten Zustand. Siehe meine Antwort Das Ablehnen eines Presented View Controllers,
in dem ich diese Methode empfehle, ist klarer geschrieben:
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
In Ihrem Fall müssen Sie sicherstellen, dass die gesamte Steuerung in erfolgt mainVC
. Sie sollten einen Delegaten verwenden, um die richtige Nachricht von ViewController1 an MainViewController zurückzusenden, damit mainVC VC1 schließen und dann VC2 präsentieren kann.
Fügen Sie in VC2 VC1 ein Protokoll in Ihre .h-Datei über dem @interface ein:
@protocol ViewController1Protocol <NSObject>
- (void)dismissAndPresentVC2;
@end
und unten in derselben Datei im Abschnitt @interface deklarieren Sie eine Eigenschaft, die den Delegatenzeiger enthält:
@property (nonatomic,weak) id <ViewController1Protocol> delegate;
In der VC1 .m-Datei sollte die Entlassungsschaltflächenmethode die Delegatenmethode aufrufen
- (IBAction)buttonPressedFromVC1:(UIButton *)sender {
[self.delegate dissmissAndPresentVC2]
}
Legen Sie es jetzt in mainVC als VC1-Delegat fest, wenn Sie VC1 erstellen:
- (IBAction)present1:(id)sender {
ViewController1* vc = [[ViewController1 alloc] initWithNibName:@"ViewController1" bundle:nil];
vc.delegate = self;
[self present:vc];
}
und implementieren Sie die Delegate-Methode:
- (void)dismissAndPresent2 {
[self dismissViewControllerAnimated:NO completion:^{
[self present2:nil];
}];
}
present2:
kann die gleiche Methode sein wie Ihre VC2Pressed:
Button-IBAction-Methode. Beachten Sie, dass es vom Abschlussblock aufgerufen wird, um sicherzustellen, dass VC2 erst angezeigt wird, wenn VC1 vollständig geschlossen wurde.
Sie wechseln jetzt von VC1-> VCMain-> VC2, sodass wahrscheinlich nur einer der Übergänge animiert werden soll.
aktualisieren
In Ihren Kommentaren drücken Sie Ihre Überraschung über die Komplexität aus, die erforderlich ist, um eine scheinbar einfache Sache zu erreichen. Ich versichere Ihnen, dieses Delegationsmuster ist für einen Großteil von Objective-C und Cocoa so zentral, und dieses Beispiel ist so einfach wie möglich, dass Sie sich wirklich die Mühe machen sollten, sich damit vertraut zu machen.
In Apples Programming - View - Controller - Führer sie haben , dies zu sagen :
Verwerfen eines Presented View Controllers
Wenn es an der Zeit ist, einen Controller für präsentierte Ansichten zu schließen, besteht der bevorzugte Ansatz darin, den Controller für präsentierte Ansichten entlassen zu lassen. Mit anderen Worten, wann immer möglich, sollte derselbe Ansichtscontroller, der den Ansichtscontroller vorgestellt hat, auch die Verantwortung für die Entlassung übernehmen. Obwohl es verschiedene Techniken gibt, um den Controller für die präsentierende Ansicht zu benachrichtigen, dass sein Controller für die präsentierte Ansicht entlassen werden sollte, ist die Delegierung die bevorzugte Technik. Weitere Informationen finden Sie unter „Verwenden der Delegierung zur Kommunikation mit anderen Controllern“.
Wenn Sie wirklich überlegen, was Sie erreichen möchten und wie Sie vorgehen, werden Sie feststellen, dass das Versenden von Nachrichten an Ihren MainViewController, um die gesamte Arbeit zu erledigen, der einzig logische Ausweg ist, da Sie keinen NavigationController verwenden möchten. Wenn Sie tun eine NavController verwenden, sind in der Tat Sie ‚Delegieren‘, auch wenn nicht explizit auf die NavController die ganze Arbeit zu tun. Es muss ein Objekt geben, das zentral verfolgt, was mit Ihrer VC-Navigation vor sich geht, und Sie benötigen eine Kommunikationsmethode, unabhängig davon, was Sie tun.
In der Praxis ist der Rat von Apple ein wenig extrem. In normalen Fällen müssen Sie keinen dedizierten Delegaten und keine dedizierte Methode festlegen, auf die Sie sich verlassen können. [self presentingViewController] dismissViewControllerAnimated:
In Fällen wie Ihrem möchten Sie, dass Ihre Entlassung andere Auswirkungen auf die Fernbedienung hat Objekte, auf die Sie achten müssen.
Hier ist etwas, das Sie sich vorstellen können , ohne den ganzen Aufwand der Delegierten zu arbeiten ...
- (IBAction)dismiss:(id)sender {
[[self presentingViewController] dismissViewControllerAnimated:YES
completion:^{
[self.presentingViewController performSelector:@selector(presentVC2:)
withObject:nil];
}];
}
Nachdem wir den präsentierenden Controller gebeten haben, uns zu entlassen, haben wir einen Abschlussblock, der eine Methode im PresentingViewController aufruft, um VC2 aufzurufen. Kein Delegierter erforderlich. (Ein großes Verkaufsargument von Blöcken ist, dass sie unter diesen Umständen den Bedarf an Delegierten verringern). In diesem Fall stehen jedoch einige Dinge im Weg ...
- In VC1 wissen Sie nicht , dass mainVC die Methode implementiert. Dies
present2
kann zu schwer zu debuggenden Fehlern oder Abstürzen führen. Delegierte helfen Ihnen, dies zu vermeiden.
- Sobald VC1 entlassen wurde, ist es nicht wirklich möglich, den Abschlussblock auszuführen ... oder? Bedeutet self.presentingViewController noch etwas? Sie wissen es nicht (ich auch nicht) ... mit einem Delegierten haben Sie diese Unsicherheit nicht.
- Wenn ich versuche, diese Methode auszuführen, hängt sie nur ohne Warnung oder Fehler.
Also bitte ... nehmen Sie sich Zeit, um Delegation zu lernen!
update2
In Ihrem Kommentar haben Sie es geschafft, dass dies funktioniert, indem Sie dies im VC2-Handler für die Schaltfläche "Entlassen" verwenden:
[self.view.window.rootViewController dismissViewControllerAnimated:YES completion:nil];
Dies ist sicherlich viel einfacher, führt jedoch zu einer Reihe von Problemen.
Enge Kopplung
Sie verdrahten Ihre viewController-Struktur fest miteinander. Wenn Sie beispielsweise vor mainVC einen neuen viewController einfügen, wird Ihr erforderliches Verhalten unterbrochen (Sie würden zum vorherigen navigieren). In VC1 mussten Sie auch VC2 # importieren. Daher haben Sie ziemlich viele Abhängigkeiten, was die OOP / MVC-Ziele verletzt.
Bei Verwendung von Delegaten müssen weder VC1 noch VC2 etwas über mainVC oder seine Vorgeschichte wissen, sodass wir alles lose gekoppelt und modular halten.
Speicher
VC1 ist nicht verschwunden, Sie halten noch zwei Zeiger darauf:
presentedViewController
Eigentum von mainVC
presentingViewController
Eigentum von VC2
Sie können dies testen, indem Sie sich anmelden und dies auch einfach über VC2 tun
[self dismissViewControllerAnimated:YES completion:nil];
Es funktioniert immer noch und bringt Sie zurück zu VC1.
Das scheint mir ein Speicherverlust zu sein.
Der Hinweis darauf liegt in der Warnung, die Sie hier erhalten:
[self presentViewController:vc2 animated:YES completion:nil];
[self dismissViewControllerAnimated:YES completion:nil];
// Attempt to dismiss from view controller <VC1: 0x715e460>
// while a presentation or dismiss is in progress!
Die Logik bricht, wie Sie versuchen , die Präsentation VC zu entlassen , von denen VC2 ist die präsentiert VC. Die zweite Nachricht wird nicht wirklich ausgeführt - vielleicht passiert etwas, aber Sie haben immer noch zwei Zeiger auf ein Objekt, von dem Sie dachten, Sie hätten es losgeworden. ( Bearbeiten - Ich habe dies überprüft und es ist nicht so schlimm, beide Objekte verschwinden, wenn Sie zu mainVC zurückkehren. )
Das ist eine ziemlich langatmige Art zu sagen - bitte verwenden Sie Delegierte. Wenn es hilft, habe ich das Muster hier noch einmal kurz beschrieben:
Ist es immer eine schlechte Praxis, einen Controller in einem Construtor zu übergeben?
Update 3
Wenn Sie Delegierte wirklich vermeiden möchten, ist dies der beste Ausweg:
In VC1:
[self presentViewController:VC2
animated:YES
completion:nil];
Aber nicht alles abtun ... wie wir festgestellt, dass es nicht sowieso passieren.
In VC2:
[self.presentingViewController.presentingViewController
dismissViewControllerAnimated:YES
completion:nil];
Da wir (wissen), dass wir VC1 nicht entlassen haben, können wir über VC1 zu MainVC zurückkehren. MainVC entlässt VC1. Da VC1 weg ist, wird es mit VC2 präsentiert, sodass Sie wieder in einem sauberen Zustand bei MainVC sind.
Es ist immer noch stark gekoppelt, da VC1 über VC2 Bescheid wissen muss und VC2 wissen muss, dass es über MainVC-> VC1 erreicht wurde, aber es ist das Beste, was Sie ohne ein wenig explizite Delegierung erhalten werden.