Що насправді робить addChildViewController?


102

Я просто вперше занурюю ноги в розробку iOS, і одне з перших, що мені довелося зробити, - це реалізувати користувальницький контролер перегляду контейнерів - давайте його назвати SideBarViewController- який заміняє, який із кількох можливих контролерів подання даних на нього показує, майже точно як стандартний контролер Tab Bar . (Це майже контролер панелі вкладок, але із прихованим бічним меню замість панелі вкладок.)

Відповідно до інструкцій в документації Apple, я дзвоню addChildViewControllerщоразу, коли я додаю до свого контейнера дочірню програму ViewController. Мій код для заміни поточного контролера дитячого перегляду показаний SideBarViewControllerприблизно таким чином:

- (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];
}

Тоді я почав намагатися зрозуміти, що addChildViewControllerтут робиться, і зрозумів, що поняття не маю. Окрім того, щоб вставити нове ViewControllerв .childViewControllersмасив, воно, здається, не вплине ні на що. Дії та торгові точки з точки зору дитячого контролера до дитячого контролера, який я встановив на дошці, все ще спрацюють добре, навіть якщо я ніколи не дзвоню addChildViewController, і не можу уявити, що ще це може вплинути.

Дійсно, якщо я перепишу свій код, щоб не дзвонити addChildViewController, а замість цього виглядаю так ...

- (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;
}

... тоді моя програма все ще працює чудово, наскільки я можу сказати!

Документація Apple не проливає багато світла на те, що addChildViewControllerробить, або чому ми повинні так називати. В даний час весь опис відповідного опису того, що робить метод або для чого його слід використовувати у своєму розділі в UIViewControllerДовідковому класі , є:

Додає заданий контролер подання в якості дитини. ... Цей метод призначений лише для виклику реалізацією спеціального контролера перегляду контейнерів. Якщо ви перекриєте цей метод, ви повинні викликати супер у своїй реалізації.

На цій же сторінці є і цей параграф раніше:

Ваш контролер подання контейнерів повинен асоціювати контролер дочірнього виду з собою перед тим, як додати кореневий вигляд дитини до ієрархії перегляду. Це дозволяє iOS правильно маршрутизувати події до контролерів дитячого перегляду та представлень, якими керують контролери. Аналогічно, після видалення кореневого подання дитини з його ієрархії подання, він повинен відключити цей контролер дочірнього виду від себе. Щоб зробити або порушити ці асоціації, ваш контейнер викликає конкретні методи, визначені базовим класом. Ці методи не призначені для виклику клієнтами вашого контейнерного класу; вони повинні використовуватися лише реалізацією вашого контейнера, щоб забезпечити очікувану поведінку утримання.

Ось основні методи, які вам можуть знадобитися зателефонувати:

addChildViewController:
deleteFromParentViewController
willMoveToParentViewController:
didMoveToParentViewController:

але він не дає жодної підказки про те, про що говорять "події" або "очікувана поведінка стримування", або чому (або навіть коли) називати ці методи "суттєвим".

Приклади користувальницьких контролерів перегляду контейнерів у розділі «Користувальницькі контролери перегляду контейнерів» документації Apple називають цей метод, тому я припускаю, що він виконує якусь важливу мету, а не просто перескакувати дочірній ViewController на масив, але я не можу зрозуміти що таке ціль. Що робить цей метод, і чому я повинен його називати?


3
Сторінка відеозаписів Apple WWDC 2011 року на цій темі пройшла чудовий сеанс ("Запровадження UIViewController Containment").
Алладініан

Відповіді:


94

Мене теж цікавило це питання. Я переглянув сесію 102 відеозаписів WWDC 2011 року і містер-контролер, Брюс Д. Ніло , сказав це:

viewWillAppear:, viewDidAppear:тощо не мають нічого спільного addChildViewController:. Все, що addChildViewController:потрібно зробити - це сказати "Цей контролер подання - це дитина", і це не має нічого спільного з зовнішнім виглядом. Коли вони дзвонять, пов'язано з тим, коли погляди переміщуються в ієрархію вікон і виходять з них.

Тож здається, що дзвінка робити addChildViewController:дуже мало. Побічні ефекти дзвінка є важливою частиною. Вони походять від відносин parentViewControllerі childViewControllers. Ось кілька побічних ефектів, які я знаю:

  • Перенаправлення зовнішнього вигляду до контролерів дитячого перегляду
  • Методи обертання вперед
  • (Можливо) попередження переадресації пам'яті
  • Уникання непослідовних ієрархій ВК, особливо в тих випадках, transitionFromViewController:toViewController:…коли обом ДК потрібно мати одного і того ж батьківського
  • Дозволити контролерам перегляду спеціальних контейнерів брати участь у збереженні та відновленні штату
  • Бере участь у ланцюжку відповідей
  • Підключення до navigationController, tabBarControllerі т.д. властивості

Це сеанс 102, а не 101
SeanChense

+1 для ланцюжка відповідей. addChildViewController потрібно , якщо ви хочете отримувати події торкання на підвид належить дитині UIViewController
charlieb

108

Я думаю, що приклад вартий тисячі слів.

Я працював над програмою бібліотеки і хотів показати приємний вид блокнота, який з’являється, коли користувач хоче додати нотатку.

введіть тут опис зображення

Спробувавши деякі рішення, я придумав власне власне рішення, щоб показати блокнот. Отже, коли я хочу показати блокнот, я створюю новий екземпляр NotepadViewControllerі додаю його кореневий вигляд як підвід до основного перегляду. Все йде нормально.

Потім я помітив, що зображення блокнота частково приховано під клавіатурою в ландшафтному режимі.

введіть тут опис зображення

Тому я хотів змінити зображення блокнота і змінити його вгору. І для цього я написав належний код у willAnimateRotationToInterfaceOrientation:duration:методі, але коли я запустив додаток, нічого не сталося! І після налагодження я помітив, що жоден із UIViewControllerметодів обертання росіян насправді не викликається NotepadViewController. Викликаються лише ті методи в контролері основного виду.

Щоб вирішити це, мені потрібно було викликати всі методи NotepadViewControllerвручну, коли вони викликаються в головному контролері перегляду. Це незабаром ускладнить справи та створить додаткову залежність між непов'язаними компонентами в додатку.

Це було раніше, ще до того, як було введено поняття контролерів дитячого зору. Але тепер вам потрібно лише до addChildViewControllerголовного контролера перегляду, і все буде працювати так, як очікувалося, без більшої ручної роботи.

Редагувати: Є дві категорії подій, які передаються контролерам дочірнього перегляду:

1- Способи зовнішнього вигляду:

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

2- Методи обертання:

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

Ви також можете керувати категоріями подій, які ви хочете переадресувати автоматично, змінивши shouldAutomaticallyForwardRotationMethodsі shouldAutomaticallyForwardAppearanceMethods.


З документації та після швидкого тестування я не думаю, що є якась інша подія, яка передається лише у випадку, якщо ви addChildViewControllerпереймете на батьківський контролер.
Хеджазі

бажаю, щоб він автоматично пересилав viewWillLayoutSubviews
MobileMon

10

-[UIViewController addChildViewController:]додає лише контролер переданого перегляду в масив viewControllers, на який viewController (батьківський) хоче зберегти посилання. Насправді ви повинні самі додати ці погляди viewController на екрані, додавши їх у вигляді підпогляду іншого представлення (наприклад, подання parentViewController). Також в Interface Builder є об’єкт зручності для використання kidsViewControllers на майданчиках.

Раніше, щоб зберегти посилання на інші представлення представників, контролерами яких ви користувались переглядами, вам довелося зберігати вручну посилання на них у @properties. Наявність властивості вбудовування, як, childViewControllersотже, parentViewControllerє зручним способом керування такими взаємодіями та побудови складених viewControllers, таких як UISplitViewController, який ви знайдете в додатках iPad.

Крім того, дітиViewControllers також автоматично отримують всі системні події, які отримує батько: -viewWillAppear, -viewWillDisappear тощо. Раніше ви повинні були називати ці методи вручну на своїх "kidsViewControllers".

Це воно.


Яка ваша основа думати, що це все, що вона робить? Також ви можете надати перелік «системних подій», які отримує дитина? Пошук в Google iOS "system events"не піднімає багато зусиль; здається, це не термін, який використовує Apple?
Марк Амері

Це в основному зручний метод, який дозволяє додавати подання контролера View B як підпогляд View Controller A, але все ще має View Controller B керувати його поданням. Для того, щоб це працювало належним чином, потрібно переконатися, що View Controller B отримує системні події (читайте зворотні виклики UIViewControllerDelegate). "addChildViewController" підключає це для вас, щоб заощадити зусилля, щоб передати все вручну.
Сем Клівлов
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.