Ta linia:
[self dismissViewControllerAnimated:YES completion:nil];
nie wysyła wiadomości do siebie, w rzeczywistości wysyła wiadomość do prezentującego VC, prosząc go o odrzucenie. Przedstawiając VC, tworzysz relację między prezentującym VC a prezentowanym. Dlatego nie powinieneś niszczyć prezentującego VC podczas prezentacji (przedstawiony VC nie może odesłać tej wiadomości odrzucającej…). Ponieważ tak naprawdę nie bierzesz tego pod uwagę, pozostawiasz aplikację w stanie zdezorientowania. Zobacz moją odpowiedź Dismissing a Presented View Controller,
w którym polecam tę metodę, jest jaśniej napisana:
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
W Twoim przypadku musisz upewnić się, że cała kontrola została wykonana mainVC
. Należy użyć delegata, aby wysłać poprawny komunikat z powrotem do MainViewController z ViewController1, aby mainVC mógł odrzucić VC1, a następnie przedstawić VC2.
W VC2 VC1 dodaj protokół w swoim pliku .h powyżej @interface:
@protocol ViewController1Protocol <NSObject>
- (void)dismissAndPresentVC2;
@end
i niżej w tym samym pliku w sekcji @interface zadeklaruj właściwość do przechowywania wskaźnika delegata:
@property (nonatomic,weak) id <ViewController1Protocol> delegate;
W pliku .m VC1 metoda przycisku odrzucenia powinna wywołać metodę delegata
- (IBAction)buttonPressedFromVC1:(UIButton *)sender {
[self.delegate dissmissAndPresentVC2]
}
Teraz w mainVC ustaw go jako delegata VC1 podczas tworzenia VC1:
- (IBAction)present1:(id)sender {
ViewController1* vc = [[ViewController1 alloc] initWithNibName:@"ViewController1" bundle:nil];
vc.delegate = self;
[self present:vc];
}
i zaimplementuj metodę delegata:
- (void)dismissAndPresent2 {
[self dismissViewControllerAnimated:NO completion:^{
[self present2:nil];
}];
}
present2:
może być tą samą metodą, co metoda VC2Pressed:
przycisku IBAction. Zauważ, że jest on wywoływany z bloku uzupełniania, aby zapewnić, że VC2 nie zostanie zaprezentowany, dopóki VC1 nie zostanie całkowicie odrzucony.
Przechodzisz teraz z VC1-> VCMain-> VC2, więc prawdopodobnie będziesz chciał animować tylko jedno przejście.
aktualizacja
W swoich komentarzach wyrażasz zdziwienie złożonością wymaganą do osiągnięcia pozornie prostej rzeczy. Zapewniam cię, że ten wzorzec delegowania jest tak centralny dla większości Objective-C i Cocoa, a ten przykład jest najprostszym, jaki możesz uzyskać, że naprawdę powinieneś dołożyć starań, aby przyzwyczaić się do niego.
W Apple View Controller Programming Guide mają do powiedzenia :
Odrzucanie kontrolera widoku prezentacji
Kiedy przychodzi czas na odrzucenie kontrolera widoku prezentowanego, preferowanym podejściem jest pozwolenie kontrolerowi widoku prezentacji na odrzucenie go. Innymi słowy, gdy tylko jest to możliwe, ten sam kontroler widoku, który przedstawił kontrolera widoku, powinien również wziąć odpowiedzialność za odwołanie go. Chociaż istnieje kilka technik powiadamiania kontrolera widoku prezentującego, że jego kontroler widoku prezentowanego powinien zostać odrzucony, preferowaną techniką jest delegowanie. Aby uzyskać więcej informacji, zobacz „Używanie delegowania do komunikacji z innymi kontrolerami”.
Jeśli naprawdę zastanowisz się, co chcesz osiągnąć i jak sobie z tym poradzisz, zdasz sobie sprawę, że wysyłanie wiadomości do MainViewController w celu wykonania całej pracy jest jedynym logicznym wyjściem, biorąc pod uwagę, że nie chcesz używać NavigationController. Jeśli zrobić użyć NavController w efekcie jesteś „delegowanie”, nawet jeśli nie wprost, do navController zrobić wszystkie prace. Musi istnieć jakiś obiekt, który centralnie śledzi to, co dzieje się z Twoją nawigacją VC, i potrzebujesz jakiejś metody komunikacji z nim, cokolwiek robisz.
W praktyce rada Apple jest trochę ekstremalna ... w normalnych przypadkach nie musisz wyznaczać dedykowanego delegata i metody, na której możesz polegać [self presentingViewController] dismissViewControllerAnimated:
- to w przypadkach takich jak twój chcesz, aby twoje zwolnienie miało inny wpływ na pilota przedmioty, na które musisz uważać.
Oto coś, co możesz sobie wyobrazić, aby pracować bez kłopotów ze strony delegata ...
- (IBAction)dismiss:(id)sender {
[[self presentingViewController] dismissViewControllerAnimated:YES
completion:^{
[self.presentingViewController performSelector:@selector(presentVC2:)
withObject:nil];
}];
}
Po poproszeniu kontrolera Presentation o zwolnienie nas, mamy blok uzupełniania, który wywołuje metodę w PresentViewController w celu wywołania VC2. Nie potrzeba żadnego delegata. (Dużą zaletą bloków jest to, że zmniejszają potrzebę delegowania w takich okolicznościach). Jednak w tym przypadku kilka rzeczy staje na przeszkodzie ...
- w VC1 nie wiesz, że mainVC implementuje metodę
present2
- możesz skończyć z trudnymi do debugowania błędami lub awariami. Delegaci pomogą ci tego uniknąć.
- po odrzuceniu VC1 tak naprawdę nie ma go w pobliżu, aby wykonać blok uzupełniania ... czy tak jest? Czy self.presentingViewController już coś znaczy? Nie wiesz (ja też nie) ... z delegatem nie masz tej niepewności.
- Kiedy próbuję uruchomić tę metodę, po prostu zawiesza się bez ostrzeżenia i błędów.
Więc proszę ... poświęć trochę czasu na naukę delegacji!
aktualizacja2
W swoim komentarzu udało Ci się to zadziałać, używając tego w programie obsługi przycisku VC2:
[self.view.window.rootViewController dismissViewControllerAnimated:YES completion:nil];
Jest to z pewnością znacznie prostsze, ale pozostawia wiele problemów.
Mocne sprzężenie
Twórz ze sobą sztywne okablowanie struktury viewController. Na przykład, jeśli chcesz wstawić nowy kontroler viewController przed mainVC, Twoje wymagane zachowanie ulegnie awarii (przejdziesz do poprzedniego). W VC1 musiałeś również #importować VC2. Dlatego masz sporo współzależności, co łamie cele OOP / MVC.
Używając delegatów, ani VC1, ani VC2 nie muszą nic wiedzieć o mainVC ani o jego poprzednikach, więc zachowujemy wszystko luźno powiązane i modułowe.
Pamięć
VC1 nie zniknęła, nadal masz do niej dwie wskazówki:
presentedViewController
właściwość mainVC
presentingViewController
Własność VC2
Możesz to przetestować, logując się, a także robiąc to z VC2
[self dismissViewControllerAnimated:YES completion:nil];
Nadal działa, nadal przenosi Cię z powrotem do VC1.
Wydaje mi się, że to wyciek pamięci.
Wskazówka do tego znajduje się w ostrzeżeniu, które otrzymujesz:
[self presentViewController:vc2 animated:YES completion:nil];
[self dismissViewControllerAnimated:YES completion:nil];
Logika się psuje, ponieważ próbujesz odrzucić prezentujący VC, którego VC2 jest prezentowanym VC. Druga wiadomość tak naprawdę nie jest wykonywana - cóż, być może coś się dzieje, ale nadal pozostają dwa wskaźniki do obiektu, o którym myślałeś, że się pozbyłeś. ( edytuj - sprawdziłem to i nie jest tak źle, oba obiekty znikają, gdy wrócisz do mainVC )
To dość rozwlekły sposób powiedzenia - proszę, skorzystajcie z delegatów. Jeśli to pomaga, przedstawiłem tutaj kolejny krótki opis wzoru:
Czy przekazanie kontrolera u konstruktora jest zawsze złą praktyką?
aktualizacja 3
Jeśli naprawdę chcesz uniknąć delegatów, może to być najlepsze wyjście:
W VC1:
[self presentViewController:VC2
animated:YES
completion:nil];
Ale nie odrzucaj niczego ... jak ustaliliśmy, tak naprawdę to się nie zdarza.
W VC2:
[self.presentingViewController.presentingViewController
dismissViewControllerAnimated:YES
completion:nil];
Ponieważ (wiemy) nie odrzuciliśmy VC1, możemy wrócić przez VC1 do MainVC. MainVC odrzuca VC1. Ponieważ VC1 zniknął, przedstawiono VC2 razem z nim, więc jesteś z powrotem w MainVC w stanie czystym.
Nadal jest silnie sprzężony, ponieważ VC1 musi wiedzieć o VC2, a VC2 musi wiedzieć, że został osiągnięty przez MainVC-> VC1, ale jest to najlepsze, co uzyskasz bez odrobiny wyraźnej delegacji.