Po raz pierwszy zanurzam się w programowaniu na iOS i jedną z pierwszych rzeczy, które musiałem zrobić, jest zaimplementowanie niestandardowego kontrolera widoku kontenera - nazwijmy to SideBarViewController
- który zamienia, który z kilku możliwych kontrolerów widoku podrzędnego to pokazuje, prawie dokładnie jak standardowy kontroler paska zakładek . (To prawie kontroler paska kart, ale z ukrytym menu bocznym zamiast paska kart.)
Zgodnie z instrukcjami w dokumentacji Apple dzwonię addChildViewController
za każdym razem, gdy dodam element podrzędny ViewController do mojego kontenera. Mój kod do zamiany bieżącego kontrolera widoku podrzędnego, który SideBarViewController
wygląda następująco:
- (void)showViewController:(UIViewController *)newViewController {
UIViewController* oldViewController = [self.childViewControllers
objectAtIndex:0];
[oldViewController removeFromParentViewController];
[oldViewController.view removeFromSuperview];
newViewController.view.frame = CGRectMake(
0, 0, self.view.frame.size.width, self.view.frame.size.height
);
[self addChildViewController: newViewController];
[self.view addSubview: newViewController.view];
}
Potem zacząłem się zastanawiać, co addChildViewController
tu robi, i zdałem sobie sprawę, że nie mam pojęcia. Oprócz umieszczania nowego ViewController
w .childViewControllers
tablicy, wydaje się, że nie ma to żadnego wpływu. Działania i wyjścia z widoku kontrolera podrzędnego do kontrolera podrzędnego, które ustawiłem w scenorysie, nadal działają dobrze, nawet jeśli nigdy nie dzwonię addChildViewController
, i nie mogę sobie wyobrazić, na co jeszcze może to wpłynąć.
Rzeczywiście, jeśli przepiszę swój kod, aby nie wywoływał addChildViewController
, a zamiast tego wyglądał tak ...
- (void)showViewController:(UIViewController *)newViewController {
// Get the current child from a member variable of `SideBarViewController`
UIViewController* oldViewController = currentChildViewController;
[oldViewController.view removeFromSuperview];
newViewController.view.frame = CGRectMake(
0, 0, self.view.frame.size.width, self.view.frame.size.height
);
[self.view addSubview: newViewController.view];
currentChildViewController = newViewController;
}
... moja aplikacja nadal działa doskonale, o ile wiem!
Dokumentacja Apple nie rzuca zbyt wiele światła na to addChildViewController
, co robi, ani dlaczego powinniśmy to nazywać. Cały zakres stosownego opisu tego, co robi ta metoda lub dlaczego powinna być stosowana w jej sekcji w opisie UIViewController
klasy, jest obecnie:
Dodaje podany kontroler widoku jako element podrzędny. ... Ta metoda jest przeznaczona do wywołania tylko przez implementację niestandardowego kontrolera widoku kontenera. Jeśli zmienisz tę metodę, musisz wywołać super w swojej implementacji.
Jest też ten akapit wcześniej na tej samej stronie:
Kontroler widoku kontenera musi skojarzyć kontroler widoku podrzędnego ze sobą przed dodaniem widoku głównego elementu podrzędnego do hierarchii widoków. Dzięki temu system iOS może prawidłowo kierować zdarzenia do kontrolerów widoku podrzędnego i widoków, którymi zarządzają. Podobnie, po usunięciu głównego widoku podrzędnego z hierarchii widoków, powinien odłączyć ten podrzędny kontroler widoku od siebie. Aby utworzyć lub przerwać te skojarzenia, kontener wywołuje określone metody zdefiniowane przez klasę bazową. Te metody nie są przeznaczone do wywoływania przez klientów klasy kontenera; mają być używane tylko przez implementację twojego kontenera, aby zapewnić oczekiwane zachowanie powstrzymujące.
Oto podstawowe metody, z których możesz skorzystać:
addChildViewController:
removeFromParentViewController
willMoveToParentViewController:
didMoveToParentViewController:
ale nie daje żadnej wskazówki co do tego, jakie są „zdarzenia” lub „oczekiwane zachowanie powstrzymujące”, o którym mowa, ani dlaczego (lub nawet kiedy) wywołanie tych metod jest „niezbędne”.
Wszystkie przykłady niestandardowych kontrolerów widoku kontenera w sekcji „Niestandardowe kontrolery widoku kontenera” w dokumentacji Apple wywołują tę metodę, więc zakładam, że służy ona do jakiegoś ważnego celu poza zwykłym umieszczeniem elementu podrzędnego ViewController na tablicy, ale nie mogę zrozumieć co to jest za cel. Co robi ta metoda i dlaczego mam ją nazywać?
źródło
Odpowiedzi:
Też się zastanawiałem nad tym pytaniem. Obejrzałem sesję 102 filmów z WWDC 2011 i Mr. View Controller, Bruce D. Nilo , powiedział:
Wydaje się więc, że wezwanie
addChildViewController:
robi bardzo niewiele. Skutki uboczne rozmowy są ważną częścią. Pochodzą z relacjiparentViewController
ichildViewControllers
. Oto kilka znanych mi efektów ubocznych:transitionFromViewController:toViewController:…
oba VC muszą mieć tego samego rodzicanavigationController
,tabBarController
itp właściwościźródło
Myślę, że przykład jest wart tysiąca słów.
Pracowałem nad aplikacją biblioteczną i chciałem pokazać ładny widok notatnika, który pojawia się, gdy użytkownik chce dodać notatkę.
Po wypróbowaniu kilku rozwiązań ostatecznie wymyśliłem własne niestandardowe rozwiązanie do wyświetlania notatnika. Więc kiedy chcę pokazać notatnik, tworzę nową instancję
NotepadViewController
i dodaję jej widok główny jako widok podrzędny do widoku głównego. Na razie w porządku.Potem zauważyłem, że obraz notatnika jest częściowo ukryty pod klawiaturą w trybie poziomym.
Chciałem więc zmienić obraz notatnika i przesunąć go w górę. Aby to zrobić, napisałem odpowiedni kod w
willAnimateRotationToInterfaceOrientation:duration:
metodzie, ale po uruchomieniu aplikacji nic się nie stało! Po debugowaniu zauważyłem, że żadna zUIViewController
metod rotacji nie jest w rzeczywistości wywoływanaNotepadViewController
. Wywoływane są tylko te metody w głównym kontrolerze widoku.Aby rozwiązać ten problem, musiałem wywołać wszystkie metody
NotepadViewController
ręcznie, gdy są wywoływane w kontrolerze widoku głównego. To wkrótce skomplikuje sprawę i stworzy dodatkową zależność między niepowiązanymi komponentami w aplikacji.To było w przeszłości, zanim wprowadzono koncepcję kontrolerów widoku podrzędnego. Ale teraz musisz tylko przejść
addChildViewController
do głównego kontrolera widoku i wszystko będzie działać zgodnie z oczekiwaniami bez żadnej dodatkowej pracy ręcznej.Edycja: istnieją dwie kategorie zdarzeń, które są przekazywane do podrzędnych kontrolerów widoku:
1- Metody wyglądu:
2- Metody rotacji:
Możesz także kontrolować, które kategorie zdarzeń mają być automatycznie przekazywane, zastępując
shouldAutomaticallyForwardRotationMethods
ishouldAutomaticallyForwardAppearanceMethods
.źródło
addChildViewController
do kontrolera nadrzędnego.-[UIViewController addChildViewController:]
dodaje tylko przekazany kontroler widoku do tablicy viewControllers, do którego viewController (element nadrzędny) chce zachować odwołanie. W rzeczywistości należy samodzielnie dodać te widoki viewControllera na ekranie, dodając je jako podglądy innego widoku (np. Widok parentViewController). Istnieje również wygodny obiekt w Interface Builder do używania childrenViewControllers w scenorysach.Wcześniej, aby zachować odniesienie do innych kontrolerów viewControllerów, z których korzystałeś, trzeba było zachować ich ręczne odniesienie w @properties. Posiadanie wbudowanej właściwości, takiej jak
childViewControllers
iw konsekwencji,parentViewController
jest wygodnym sposobem zarządzania takimi interakcjami i tworzenia skomponowanych kontrolerów widoku, takich jak UISplitViewController, które można znaleźć w aplikacjach na iPada.Ponadto childrenViewControllers również automatycznie odbierają wszystkie zdarzenia systemowe, które otrzymuje rodzic: -viewWillAppear, -viewWillDisappear, itp. Wcześniej należało wywoływać te metody ręcznie na swoich "childrenViewControllers".
Otóż to.
źródło
iOS "system events"
nie zwymiotuje; to nie wydaje się być terminem używanym przez Apple?