Zasadniczo próbuję wywołać viewWillAppear mojego viewControllera, gdy mój widok jest odrzucany. Widok jest odrzucany przez sam widok, który wykrywa dotknięcie i wywołuje [self removeFromSuperview]; ViewController nie wywołuje samego viewWillAppear / WillDisappear / DidAppear / DidDisappear.
mahboudz
Chodziło mi o to, że próbuję wywołać viewWillDisappear, ponieważ mój widok jest odrzucany.
Tak, superviewjest to widok, który zawiera Twój widok. Twój widok nie powinien wiedzieć, który dokładnie jest jego kontrolerem widoku, ponieważ złamałoby to zasady MVC.
Z drugiej strony kontroler wie, za który widok jest odpowiedzialny ( self.view = myView) i zwykle ten widok deleguje metody / zdarzenia do obsługi do kontrolera.
Zazwyczaj zamiast wskaźnika do widoku powinieneś mieć wskaźnik do kontrolera, który z kolei może albo wykonać jakąś logikę sterującą, albo przekazać coś do swojego widoku.
Nie jestem pewien, czy złamałoby to zasady MVC. W dowolnym momencie widok ma tylko jeden kontroler widoku. Możliwość dotarcia do niego w celu przekazania do niego wiadomości powinna być funkcją automatyczną, a nie taką, nad którą trzeba pracować (dodając właściwość do śledzenia). To samo można powiedzieć o poglądach: dlaczego musisz wiedzieć, kim jesteś dzieckiem? Albo czy istnieją inne poglądy na temat rodzeństwa. Jednak istnieją sposoby na zdobycie tych obiektów.
mahboudz
Masz trochę racji co do widoku, wiedząc o jego rodzicu, nie jest to bardzo jasna decyzja projektowa, ale jest już ustalona, aby wykonać pewne czynności, używając bezpośrednio zmiennej członkowskiej superview (sprawdź typ rodzica, usuń z rodzica itp.). Pracując ostatnio z PureMVC, stałem się trochę bardziej wybredny w kwestii abstrakcji projektu :) Chciałbym zrobić paralelę między klasami UIView i UIViewController iPhone'a a klasami View i Mediator PureMVC - w większości przypadków klasa View nie musi wiedzieć o swojej obsłudze / interfejsie MVC (UIViewController / Mediator).
Dimitar Dimitrov
9
Słowo kluczowe: „większość”.
Glenn Maynard
278
Z UIResponderdokumentacji dla nextResponder:
Klasa UIResponder nie przechowuje ani nie ustawia automatycznie następnego respondera, zamiast tego domyślnie zwraca nil. Podklasy muszą przesłonić tę metodę, aby ustawić następny responder. UIView implementuje tę metodę, zwracając obiekt UIViewController, który nim zarządza (jeśli taki posiada) lub jego superview (jeśli nie) ; UIViewController implementuje metodę, zwracając superview swojego widoku; UIWindow zwraca obiekt aplikacji, a UIApplication zwraca nil.
Tak więc, jeśli powtarzasz widok, nextResponderdopóki nie stanie się on typu UIViewController, masz nadrzędny atrybut viewController dowolnego widoku.
Zauważ, że nadal może nie mieć nadrzędnego kontrolera widoku. Ale tylko wtedy, gdy widok nie jest częścią hierarchii widoków viewControllera.
Tylko jedno: jeśli martwisz się zanieczyszczeniem kategorii, po prostu zdefiniuj je jako funkcję statyczną, a nie makro. Również brutalne typowanie jest niebezpieczne, a ponadto makro może nie być poprawne, ale nie jestem pewien.
mojuba
Makro działa, osobiście używam tylko wersji makra. Rozpoczynam wiele projektów i mam nagłówek, w którym po prostu wpadam wszędzie z kilkoma funkcjami narzędzi makr. Oszczędza mi czas. Jeśli nie lubisz makr, możesz dostosować je do funkcji, ale funkcje statyczne wydają się nudne, ponieważ musisz je umieścić w każdym pliku, którego chcesz użyć. Wygląda na to, że zamiast tego chcesz zadeklarować niestatyczną funkcję w nagłówku i zdefiniowaną w jakimś miejscu w pliku .m?
mxcl
Miło jest uniknąć niektórych delegatów lub powiadomień. Dzięki!
Ferran Maylinch
Powinieneś uczynić to przedłużeniem UIResponder;). Bardzo budujący post.
ScottyBlades
32
@andrey odpowiedź w jednej linii (testowane w Swift 4.1 ):
parentViewControllernie można zdefiniować, publicjeśli rozszerzenie znajduje się w tym samym pliku, co Twoje, UIViewmożesz je ustawić fileprivate, kompiluje się, ale nie działa! 😐
Działa dobrze. Użyłem tego do akcji przycisku Wstecz wewnątrz wspólnego pliku nagłówkowego .xib.
McDonal_11
23
Tylko do celów debugowania można wywoływać _viewDelegatewidoki, aby uzyskać ich kontrolery widoku. To jest prywatny interfejs API, więc nie jest bezpieczny dla App Store, ale do debugowania jest przydatny.
Inne przydatne metody:
_viewControllerForAncestor- pobierz pierwszy kontroler, który zarządza widokiem w łańcuchu superview. (dzięki n00neimp0rtant)
_rootAncestorViewController - pobierz kontroler nadrzędny, którego hierarchia widoków jest aktualnie ustawiona w oknie.
Właśnie dlatego doszedłem do tego pytania. Najwyraźniej „nextResponder” robi to samo, ale doceniam wgląd, jaki zapewnia ta odpowiedź. Rozumiem i lubię MVC, ale debugowanie to inna sprawa!
mbm29414
4
Wygląda na to, że działa to tylko na głównym widoku kontrolera widoku, a nie na żadnych jego podglądach podrzędnych. _viewControllerForAncestorprzejdzie w górę nadzorów, aż znajdzie pierwszy, który należy do kontrolera widoku.
n00neimp0rtant
Dzięki @ n00neimp0rtant! Głosuję za tą odpowiedzią, aby ludzie widzieli Twój komentarz.
eyuelt
Zaktualizuj odpowiedź, używając większej liczby metod.
Leo Natan
1
Jest to przydatne podczas debugowania.
evanchin
10
Aby uzyskać odniesienie do UIViewController mającego UIView, możesz utworzyć rozszerzenie UIResponder (która jest superklasą dla UIView i UIViewController), które pozwala przejść w górę przez łańcuch responder i w ten sposób dotrzeć do UIViewController (w przeciwnym razie zwraca nil).
Myślę, że możesz przekazać kran do kontrolera widoku i pozwolić mu się tym zająć. To bardziej akceptowalne podejście. Jeśli chodzi o dostęp do kontrolera widoku z jego widoku, powinieneś zachować odwołanie do kontrolera widoku, ponieważ nie ma innego sposobu. Zobacz ten wątek, może pomóc:
Dostęp do kontrolera widoku z widoku
Jeśli masz kilka widoków i jeden zamyka wszystkie widoki i musisz wywołać funkcję viewWillDisappear, czy nie byłoby łatwiej, aby ten widok wykrył stuknięcie, niż przekazanie kranu kontrolerowi widoku i sprawdzenie przez kontroler widoku ze wszystkimi widokami, aby zobaczyć, który z nich został podsłuchany?
Niestety, jest to niemożliwe, chyba że podklasujesz widok i nie udostępnisz mu właściwości instancji lub podobnej, która przechowuje odwołanie do kontrolera widoku w nim po dodaniu widoku do sceny ...
W większości przypadków - bardzo łatwo jest obejść pierwotny problem tego postu, ponieważ większość kontrolerów widoku to jednostki dobrze znane programiście, który był odpowiedzialny za dodanie jakichkolwiek podwidoków do widoku ViewControllera ;-) Dlatego domyślam się, że Apple nigdy nie zawracał sobie głowy dodaniem tej własności
Odpowiedzi:
Tak,
superview
jest to widok, który zawiera Twój widok. Twój widok nie powinien wiedzieć, który dokładnie jest jego kontrolerem widoku, ponieważ złamałoby to zasady MVC.Z drugiej strony kontroler wie, za który widok jest odpowiedzialny (
self.view = myView
) i zwykle ten widok deleguje metody / zdarzenia do obsługi do kontrolera.Zazwyczaj zamiast wskaźnika do widoku powinieneś mieć wskaźnik do kontrolera, który z kolei może albo wykonać jakąś logikę sterującą, albo przekazać coś do swojego widoku.
źródło
Z
UIResponder
dokumentacji dlanextResponder
:Tak więc, jeśli powtarzasz widok,
nextResponder
dopóki nie stanie się on typuUIViewController
, masz nadrzędny atrybut viewController dowolnego widoku.Zauważ, że nadal może nie mieć nadrzędnego kontrolera widoku. Ale tylko wtedy, gdy widok nie jest częścią hierarchii widoków viewControllera.
Rozszerzenie Swift 3 i Swift 4.1 :
Rozszerzenie Swift 2:
Kategoria celu-C:
To makro pozwala uniknąć zanieczyszczenia kategorii:
źródło
UIResponder
;). Bardzo budujący post.@andrey odpowiedź w jednej linii (testowane w Swift 4.1 ):
stosowanie:
źródło
parentViewController
nie można zdefiniować,public
jeśli rozszerzenie znajduje się w tym samym pliku, co Twoje,UIView
możesz je ustawićfileprivate
, kompiluje się, ale nie działa! 😐Tylko do celów debugowania można wywoływać
_viewDelegate
widoki, aby uzyskać ich kontrolery widoku. To jest prywatny interfejs API, więc nie jest bezpieczny dla App Store, ale do debugowania jest przydatny.Inne przydatne metody:
_viewControllerForAncestor
- pobierz pierwszy kontroler, który zarządza widokiem w łańcuchu superview. (dzięki n00neimp0rtant)_rootAncestorViewController
- pobierz kontroler nadrzędny, którego hierarchia widoków jest aktualnie ustawiona w oknie.źródło
_viewControllerForAncestor
przejdzie w górę nadzorów, aż znajdzie pierwszy, który należy do kontrolera widoku.Aby uzyskać odniesienie do UIViewController mającego UIView, możesz utworzyć rozszerzenie UIResponder (która jest superklasą dla UIView i UIViewController), które pozwala przejść w górę przez łańcuch responder i w ten sposób dotrzeć do UIViewController (w przeciwnym razie zwraca nil).
źródło
Szybki i ogólny sposób w Swift 3:
źródło
Jeśli nie znasz kodu i chcesz znaleźć ViewController odpowiadający danemu widokowi, możesz spróbować:
W większości przypadków otrzymasz UIView, ale od czasu do czasu będzie tam klasa oparta na UIViewController.
źródło
Myślę, że możesz przekazać kran do kontrolera widoku i pozwolić mu się tym zająć. To bardziej akceptowalne podejście. Jeśli chodzi o dostęp do kontrolera widoku z jego widoku, powinieneś zachować odwołanie do kontrolera widoku, ponieważ nie ma innego sposobu. Zobacz ten wątek, może pomóc: Dostęp do kontrolera widoku z widoku
źródło
Bardziej bezpieczny kod dla Swift 3.0
źródło
Niestety, jest to niemożliwe, chyba że podklasujesz widok i nie udostępnisz mu właściwości instancji lub podobnej, która przechowuje odwołanie do kontrolera widoku w nim po dodaniu widoku do sceny ...
W większości przypadków - bardzo łatwo jest obejść pierwotny problem tego postu, ponieważ większość kontrolerów widoku to jednostki dobrze znane programiście, który był odpowiedzialny za dodanie jakichkolwiek podwidoków do widoku ViewControllera ;-) Dlatego domyślam się, że Apple nigdy nie zawracał sobie głowy dodaniem tej własności
źródło
Trochę późno, ale tutaj jest rozszerzenie, które umożliwia znalezienie respondera dowolnego typu, w tym ViewController.
źródło
Jeśli ustawisz punkt przerwania, możesz wkleić to do debugera, aby wydrukować hierarchię widoków:
Gdzieś w tym bałaganie powinieneś znaleźć rodzica swojego widoku :)
źródło
recursiveDescription
drukuje tylko hierarchię widoków , a nie kontrolery widoku.