Odrzucanie kontrolera widoku prezentacji


116

Mam teoretyczne pytanie. Teraz czytam przewodnik Apple ViewController .

Oni napisali:

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 jego odwołanie. Chociaż istnieje kilka technik powiadamiania kontrolera widoku prezentującego, że jego kontroler widoku prezentowanego powinien zostać odrzucony, preferowaną techniką jest delegowanie.

Ale nie potrafię wyjaśnić, dlaczego muszę stworzyć protokół w przedstawionym VC i dodać zmienną delegata, stworzyć metodę delegata w prezentacji VC dla odrzucenia prezentowanego VC, zamiast prostego wywołania w przedstawionym widoku metoda kontrolera

[self dismissViewControllerAnimated:NO completion:nil]?

Dlaczego pierwszy wybór jest lepszy? Dlaczego Apple to poleca?

Odpowiedzi:


122

Myślę, że Apple zakrywa trochę swoje plecy, aby uzyskać potencjalnie niezdarny kawałek API.

  [self dismissViewControllerAnimated:NO completion:nil]

Właściwie to trochę skrzypce. Chociaż możesz - słusznie - wywołać to na kontrolerze prezentowanego widoku, wszystko, co robi, to przekazuje wiadomość do kontrolera widoku prezentacji. Jeśli chcesz zrobić coś więcej niż tylko odrzucenie VC, musisz to wiedzieć i musisz traktować to w taki sam sposób, jak metodę delegata - ponieważ to jest prawie to, co jest, nieco nieelastyczne metoda delegata.

Być może natknęli się na mnóstwo złego kodu przez ludzi, którzy tak naprawdę nie rozumieli, jak to się robi, stąd ich ostrożność.

Ale oczywiście, jeśli wszystko, co musisz zrobić, to odrzucić sprawę, śmiało.

Moje własne podejście jest kompromisem, przynajmniej przypomina mi o co chodzi:

  [[self presentingViewController] dismissViewControllerAnimated:NO completion:nil]

[Szybki]

  self.presentingViewController?.dismiss(animated: false, completion:nil)

26
Należy zauważyć, że używanie presentingViewControllerjest w większości bezużyteczne, ponieważ będzie odnosić się do UINavigationControllerif selfjest osadzone w jednym. W takim przypadku w ogóle nie będziesz w stanie uzyskać presentingViewController. Jednak [self dismissViewControllerAnimated:completion]nadal działa w tym przypadku. Moją sugestią byłoby dalsze używanie tego, dopóki Apple tego nie naprawi.
memmons

4
Podoba mi się, że ta odpowiedź jest nadal w pełni aktualna 3 lata później.
user1021430

1
Inną kwestią do rozważenia jest to, że kontroler widoku nie wie, jak został wyświetlony. Mogło zostać zaprezentowane, przeniesione na kontroler nawigacji, część kontrolera paska zakładek itp. Użycie delegata pozwala kontrolerowi „prezentacji” „odrzucić” kontroler widoku przy użyciu odwrotności dowolnej metody, która została użyta do jego przedstawienia.
David Smith

51

Zaktualizowano dla Swift 3

Przyszedłem tutaj, chcąc odrzucić obecny (przedstawiony) kontroler widoku. Podaję tę odpowiedź każdemu, kto przyjeżdża tutaj w tym samym celu.

Kontroler nawigacji

Jeśli używasz kontrolera nawigacyjnego, jest to dość łatwe.

Wróć do poprzedniego kontrolera widoku:

// Swift
self.navigationController?.popViewController(animated: true)

// Objective-C
[self.navigationController popViewControllerAnimated:YES];

Wróć do głównego kontrolera widoku:

// Swift
self.navigationController?.popToRootViewController(animated: true)

// Objective-C
[self.navigationController popToRootViewControllerAnimated:YES];

(Dzięki tej odpowiedzi dla Objective-C.)

Kontroler widoku modalnego

Gdy kontroler widoku jest prezentowany modalnie, możesz go odrzucić (z drugiego kontrolera widoku), wywołując

// Swift
self.dismiss(animated: true, completion: nil)

// Objective-C
[self dismissViewControllerAnimated:YES completion:nil];

Dokumentacja mówi,

Kontroler widoku prezentacji jest odpowiedzialny za odrzucenie kontrolera widoku, który przedstawił. Jeśli wywołasz tę metodę na samym kontrolerze widoku prezentowanego, UIKit prosi kontroler widoku prezentacji o obsługę odrzucenia.

Tak więc działa, aby kontroler widoku przedstawionego wywoływał go sam. Oto pełny przykład.

Delegaci

Pytanie PO dotyczyło złożoności wykorzystania delegatów do odrzucenia opinii.

Do tej pory nie potrzebowałem używać delegatów, ponieważ zwykle mam kontroler nawigacji lub kontrolery widoku modalnego, ale jeśli będę musiał używać wzorca delegata w przyszłości, dodam aktualizację.


50

Służy to do ponownego wykorzystania kontrolera widoku.

Twój kontroler widoku nie powinien przejmować się tym, czy jest prezentowany jako modalny, wypychany na kontrolerze nawigacji czy cokolwiek innego. Jeśli kontroler widoku sam się odrzuca, to zakładasz, że jest prezentowany modalnie. Nie będzie można wypchnąć tego kontrolera widoku na kontroler nawigacyjny.

Implementując protokół, pozwalasz nadrzędnemu kontrolerowi widoku zdecydować, w jaki sposób ma być prezentowany / wypychany i odrzucany / otwierany.



6

Z mojego doświadczenia wynika, że ​​przydaje się, gdy trzeba usunąć go z dowolnego kontrolera ViewController i wykonać różne zadania dla każdego kontrolera widoku, który go odrzuca. Każdy kontroler viewController, który przyjmuje protokół, może odrzucić widok na swój własny sposób. (iPad vs iPhone lub przekazywanie różnych danych podczas zamykania z różnych widoków, wywoływanie różnych metod podczas zamykania itp.)

Edytować:

Tak więc, aby wyjaśnić, jeśli wszystko, co kiedykolwiek chcesz zrobić, to odrzucić widok, nie widzę potrzeby konfigurowania protokołu delegata. Jeśli musisz zrobić inne rzeczy po usunięciu go z różnych kontrolerów widoku prezentacji, najlepszym sposobem byłoby skorzystanie z delegata.


ale jeśli nie potrzebuję „przekazywania różnych danych podczas zamykania z różnych widoków, wywoływania różnych metod podczas zamykania itp.”, czy mogę wykonać jedno małe wywołanie w metodzie kontrolera widoku prezentowanego - [samo odrzucenieViewControllerAnimated: BRAK zakończenia: zero]?
nikitahils

Pozwolenie prezenterowi na odrzucenie przedstawionego widoku daje do zrozumienia, że ​​prezenter jest w rzeczywistości gotowy i obsługuje powrót na pierwszy plan: sekwencja wykonania jest łatwa do naśladowania, a odpowiedzialność za każdą aktualizację interfejsu użytkownika jest niejawnie jasna.
Johan,

2

Cytuj z Przewodnika programowania kontrolerów firmy View , „Jak kontrolery przedstawiają inne kontrolery widoku”.

Każdy kontroler widoku w łańcuchu kontrolerów widoku prezentowanego ma wskaźniki do innych obiektów otaczających go w łańcuchu. Innymi słowy, prezentowany kontroler widoku, który przedstawia inny kontroler widoku, ma prawidłowe obiekty we właściwościach PresentViewController i PresentViewController. W razie potrzeby można użyć tych relacji do prześledzenia łańcucha kontrolerów widoku. Na przykład, jeśli użytkownik anuluje bieżącą operację, możesz usunąć wszystkie obiekty w łańcuchu, odrzucając pierwszy przedstawiony kontroler widoku. Odrzucenie kontrolera widoku odrzuca nie tylko ten kontroler widoku, ale także wszystkie kontrolery widoku, które przedstawił.

Z jednej strony zapewnia to ładną, wyważoną konstrukcję, dobre odsprzęganie itp. Ale z drugiej strony jest to bardzo praktyczne, ponieważ można szybko wrócić do określonego punktu w nawigacji.

Chociaż osobiście wolałbym raczej używać rozwijania segmentów niż próbować przechodzić wstecz przez drzewo kontrolerów widoku prezentacji , o czym Apple mówi w tym rozdziale, skąd pochodzi cytat.


2

Po pierwsze, jest to dobre podejście do kodowania. Spełnia wiele OOPzasad, np. SRP, Separacja problemów itp.

Zatem kontroler widoku prezentujący widok powinien być tym, który go odrzuca.

Na przykład firma zajmująca się obrotem nieruchomościami, która daje dom do wynajęcia, powinna być upoważniona do jego zwrotu.


2

Swift 3.0 // Szybko zamknij kontroler widoku

self.navigationController?.popViewController(animated: true)
dismiss(animated: true, completion: nil)

1

Oprócz odpowiedzi Michaela Enriqueza przychodzi mi do głowy jeszcze jeden powód, dla którego może to być dobry sposób na ochronę przed nieokreślonym stanem:

Powiedz, że ViewControllerA przedstawia ViewControllerB modalnie. Ale ponieważ być może nie napisałeś kodu dla ViewControllerA, nie znasz cyklu życia ViewControllerA. Może odrzucić 5 sekund (powiedzmy) po zaprezentowaniu kontrolera widoku, ViewControllerB.

W tym przypadku, jeśli po prostu używasz dismissViewController ViewControllerB do odrzucenia samego siebie, skończyłbyś w niezdefiniowanym stanie - być może nie awarią lub czarnym ekranem, ale stan niezdefiniowany z twojego punktu widzenia.

Gdybyś zamiast tego używał wzorca delegata, byłbyś świadomy stanu ViewControllerB i możesz zaprogramować przypadek taki jak ten, który opisałem.


1

Szybki

let rootViewController:UIViewController = (UIApplication.shared.keyWindow?.rootViewController)!

        if (rootViewController.presentedViewController != nil) {
            rootViewController.dismiss(animated: true, completion: {
                //completion block.
            })
        }

0

Jeśli używasz modalnego widoku, odrzuć.

[self dismissViewControllerAnimated:NO completion:nil];

Jak to odpowiada na pytanie: „Dlaczego pierwszy wybór jest lepszy? Dlaczego Apple go poleca?”
jww

0

To dużo bzdur. Delegacja jest w porządku, gdy jest potrzebna, ale jeśli sprawia, że ​​kod jest bardziej złożony - a tak się dzieje - musi być ku temu powód.

Jestem pewien, że Apple ma swoje powody. Ale jaśniejsze i bardziej zwięzłe jest po prostu zlecenie przedstawionego VC odrzucenia, chyba że istnieje prawdziwy powód, by postąpić inaczej i nikt tutaj do dziś nie przedstawił takiego, który widzę.

Protokoły są doskonałe, gdy są potrzebne, ale w projektowaniu zorientowanym obiektowo nigdy nie chodziło o niepotrzebną komunikację modułów ze sobą.

Tom Love (współtwórca Objective C) powiedział kiedyś, że Objective C jest „elegancki”, „mały”, „ostry” i „dobrze zdefiniowany” (w porównaniu z C ++). Łatwo mu powiedzieć. Delegowanie to przydatna funkcja, która wydaje się być nadużywana „tylko dlatego”, i chociaż lubię pracować w języku, obawiam się, że będę zmuszony używać niepotrzebnej składni, aby uczynić rzeczy bardziej złożonymi, niż jest to konieczne.


Może to początkowo zaoszczędzić trochę kodu, ale twoje podejście spowoduje wiele bólów głowy w miarę wzrostu bazy kodu. Powinieneś rozumieć zasady zorientowane obiektowo, takie jak oddzielenie problemów, w przeciwnym razie równie dobrze możesz zakodować całą aplikację w jednym dużym pliku.
Werner Altewischer

-2

Możesz zamknąć okno super widoku

self.view.superview?.window?.close()

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.