2013-06-19 7 views
83

Po raz pierwszy zanurzam się w rozwoju iOS i jedną z pierwszych rzeczy, które musiałem zrobić, jest implementacja custom container view controller - pozwala to nazwać je SideBarViewController - która zamienia który z kilku możliwych kontrolerów widoku podrzędnego to pokazuje, prawie dokładnie tak, jak standardowy kontroler paska zakładek . (Jest to dość dużo Tab Bar Controller ale z hideable bocznym menu zamiast paska kart.)Co właściwie robi addChildViewController?

zgodnie z instrukcjami zawartymi w dokumentacji firmy Apple, wzywam addChildViewController gdy dodam dziecko ViewController do mojego pojemnika. Mój kod wymieniając bieżący kontroler widoku dziecko wykazanie przez 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 próbuje dowiedzieć się, co robi tutaj addChildViewController, i zdałem sobie sprawę, że nie mam pojęcia. Poza przyklejeniem nowego ViewController w tablicy .childViewControllers, wydaje się, że nie ma żadnego wpływu na nic. Akcje i punkty sprzedaży z widoku kontrolera dziecięcego do kontrolera podrzędnego, który ustawiłem w scenorysie, nadal działają dobrze, nawet jeśli nigdy nie zadzwonię pod numer addChildViewController i nie wyobrażam sobie, na co jeszcze mógłby on wpłynąć.

Rzeczywiście, gdybym przepisać mój kod, aby nie wywołać addChildViewController i zamiast 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; 
} 

... to moja aplikacja nadal działa idealnie, więc ile mogę powiedzieć!

Dokumentacja Apple nie rzuca dużo światła na to, co robi addChildViewController lub dlaczego powinniśmy to nazwać. Cały zakres odpowiednim opisem tego, co robi i dlaczego metoda powinna być stosowana w swojej sekcji w UIViewController Class Reference jest w chwili obecnej:

dodaje podany kontrolera widoku za dziecko. ... Ta metoda jest przeznaczona tylko do wywołania przez implementację niestandardowego kontrolera widoku kontenera. Jeśli zastąpisz tę metodę, musisz wywołać super w swojej implementacji.

Istnieje również ten akapit wcześniej na tej samej stronie:

Kontener widok kontroler musi kojarzyć kontroler widoku dziecko ze sobą przed dodaniem widoku głównego dziecka do hierarchii widoku. Dzięki temu system iOS może prawidłowo kierować zdarzenia do kontrolerów podrzędnych i widoki, którymi zarządzają te kontrolery. Podobnie, po usunięciu widoku głównego dziecka z jego hierarchii widoku, powinien on odłączyć ten kontroler widoku podrzędnego od siebie. Aby je utworzyć lub je zerwać, kontener wywołuje określone metody zdefiniowane przez klasę podstawową. Te metody nie są przeznaczone do wywoływania przez klientów Twojej klasy kontenera; mają być używane tylko przez implementację kontenera w celu zapewnienia oczekiwanego zachowania ograniczającego.

Oto zasadnicze metody może trzeba zadzwonić:

addChildViewController:
removeFromParentViewController
willMoveToParentViewController:
didMoveToParentViewController:

ale nie oferuje żadnej wskazówki co do tego, co "zdarzenia" lub "oczekiwane działanie ograniczające rozprzestrzenianie", o których mówi, lub dlaczego (lub nawet kiedy) wywoływanie tych metod jest "niezbędne".

Przykłady niestandardowych kontrolerów widoku kontenerów w sekcji "Niestandardowe kontrolery kontenera" w dokumentacji Apple nazywają tę metodę, więc zakładam, że spełnia ona pewne ważne zadanie poza poprzesłaniem dziecka ViewController na tablicę, ale ja nie rozumiem, jaki jest ten cel. Co robi ta metoda i dlaczego powinienem ją nazwać?

+3

Apple [** 2011 ** WWDC] (https://developer.apple.com/videos/wwdc/2011/) strona z filmami ma sesję _great_ ("Implementowanie zabezpieczenia UIViewController") na ten temat. – Alladinian

Odpowiedz

81

Zastanawiam się również nad tym pytaniem. Patrzyłem Session 102 of the WWDC 2011 filmy i Pan View Controller, Bruce D. Nilo, powiedział:

viewWillAppear:, viewDidAppear: itp nie mają nic wspólnego z addChildViewController:. Wszystko, co robi addChildViewController:, to powiedzenie "Ten kontroler widoku jest dzieckiem tego" i nie ma nic wspólnego z wyglądem widoku. Gdy zostaną wywołane, jest związane z tym, kiedy widoki wchodzą i opuszczają hierarchię okien.

Wygląda na to, że połączenie z addChildViewController: wykonuje bardzo niewiele. Skutki uboczne połączenia są ważną częścią. Pochodzą z relacji parentViewController i childViewControllers. Oto niektóre z efektów ubocznych, które znam:

  • metody wygląd Forwarding aby zobaczyć dzieci kontrolerów
  • Forwarding metody rotacji
  • ostrzeżenia
  • (ewentualnie) Pamięć spedycja
  • Unikanie niespójne hierarchie VC, zwłaszcza w transitionFromViewController:toViewController:… gdzie oba obiekty wirtualne muszą mieć tego samego rodzica:
  • Umożliwienie niestandardowym kontrolerom widoków kontenerów udziału w konserwacji i renowacji stanów
  • aking części łańcucha reagujących
  • Sposób włączenia navigationController, tabBarController itp właściwości
+0

Sesja 102 nie 101 – SeanChense

+0

+1 dla łańcucha responder. addChildViewController jest wymagany, jeśli chcesz otrzymywać zdarzenia dotyku na podglądzie posiadanym przez dziecko UIViewController – charlieb

9

-[UIViewController addChildViewController:] dodaje tylko kontroler przekazany w widoku tylko w tablicy viewControllers, który obiekt viewController (rodzic) chce zachować odwołanie. Powinieneś faktycznie dodać widoki viewController na ekranie, dodając je jako subviews innego widoku (np. Widok parentViewController). W Konstruktorze interfejsów znajduje się również obiekt typu convenience, który umożliwia korzystanie z kontrolek childrenViewControllers w scenorysach.

Poprzednio, aby zachować odniesienie do innych kontrolek viewControllers, z których korzystano z widoków, trzeba było zachować ręczne odniesienie do nich w @ właściwości. Posiadanie wbudowanej właściwości, takiej jak childViewControllers i konsekwentnie parentViewController jest wygodnym sposobem zarządzania takimi interakcjami i budowania złożonych kontrolerów view Control, takich jak UISplitViewController, który można znaleźć w aplikacjach na iPada.

Co więcej, childrenViewControllers również automatycznie otrzymują wszystkie zdarzenia systemowe, które rodzic otrzymuje: -viewWillAppear, -viewWillDisappear, itp. Poprzednio powinieneś był ręcznie wywołać te metody na "childrenViewControllers".

To wszystko.

+0

Jaka jest Twoja podstawa do myślenia, że ​​to wszystko, co robi? Czy możesz podać listę "zdarzeń systemowych", które otrzymają dziecko? Wyszukiwarka "zdarzeń" systemu "iOS" w wyszukiwarce Google "nie wyrzuca zbyt wiele; nie wydaje się, że jest to określenie, którego używa Apple? –

+0

Jest to w zasadzie metoda wygody, która umożliwia dodanie widoku kontrolera View B jako podglądu kontrolera widoku A, ale nadal kontroler widoku B zarządza jego widokiem. Aby to działało poprawnie, musisz upewnić się, że kontroler widoku B otrzymuje zdarzenia systemowe (odczytaj wywołania zwrotne UIViewControllerDelegate). "addChildViewController" podpina ci to, aby zaoszczędzić wysiłku ręcznego przekazywania wszystkiego. –

94

Myślę, że przykład jest wart tysiąca słów.

Pracowałem nad aplikacją biblioteki i chciałem wyświetlić ładny widok notatnika, który pojawia się, gdy użytkownik chce dodać notatkę.

enter image description here

Po wypróbowaniu kilku rozwiązań, skończyło się wymyślając własne niestandardowe rozwiązania, aby pokazać notes. Kiedy więc chcę wyświetlić notatnik, tworzę nowe wystąpienie NotepadViewController i dodaję jego widok główny jako widok do widoku głównego. Jak na razie dobrze.

Następnie zauważyłem, że obraz notatnika jest częściowo ukryty pod klawiaturą w trybie poziomym.

enter image description here

Więc chciałem zmienić notatnik obraz i przesuwać go w górę. Aby to zrobić, napisałem odpowiedni kod w metodzie willAnimateRotationToInterfaceOrientation:duration:, ale kiedy uruchomiłem aplikację nic się nie stało! Po debugowaniu zauważyłem, że żadna z metod rotacji UIViewController nie jest wywoływana w NotepadViewController. Wywoływane są tylko te metody w głównym kontrolerze widoku.

Aby rozwiązać ten problem, musiałem ręcznie wywołać wszystkie metody z NotepadViewController, gdy zostaną one wywołane w głównym kontrolerem widoku. To wkrótce sprawi, że rzeczy będą skomplikowane i stworzą dodatkową zależność między niespokrewnionymi komponentami w aplikacji.

To było w przeszłości, zanim wprowadzono pojęcie kontrolerów widoku dziecka. Ale teraz, musisz tylko addChildViewController do głównego kontrolera widoku i wszystko będzie działało zgodnie z oczekiwaniami bez żadnej ręcznej pracy.

Edycja: Istnieją dwa rodzaje zdarzeń, które są przekazywane do widoku dziecko regulatorów:

1- wyglądu Metody:

- viewWillAppear: 
- viewDidAppear: 
- viewWillDisappear: 
- viewDidDisappear: 

2- Metody obrotów:

- willRotateToInterfaceOrientation:duration: 
- willAnimateRotationToInterfaceOrientation:duration: 
- didRotateFromInterfaceOrientation: 

Możesz również kontrolować, które kategorie zdarzeń chcesz automatycznie przekazywać, przesuwając shouldAutomaticallyForwardRotationMethods i shouldAutomaticallyForwardAppearanceMethods.

+0

Z dokumentacji i po wykonaniu szybkiego testu nie myślę, że istnieje jakiekolwiek inne zdarzenie, które jest przekazywane tylko, jeśli 'addChildViewController' do kontrolera nadrzędnego. – Hejazi

+0

Żądam, aby automatycznie przekazywał viewWillLayoutSubviews – MobileMon