Как работает ограничение на просмотр контроллера в iOS 5?

В сеансе 102 WWDC 2011 Apple представила View Controller Containment, которая представляет собой возможность создавать пользовательские контейнеры контроллера просмотра, аналогичные UITabBarController, UINavigationController и тому подобное.

Я несколько раз смотрел примеры. Есть шквал методов, связанных с этим рисунком, но было немного трудно понять их точно. Я собираюсь опубликовать здесь, что, по-моему, происходит, и посмотреть, подтвердит ли сообщество или не подтвердит мои подозрения.

Сценарий 1: переход от родителя к новому контроллеру родительского представления

[vc willMoveToParentViewController:self];
[self addChildViewController:vc];
[self.view addSubview:vc.view]; // or something like this.
[vc didMoveToParentViewController:self];

Должны ли первые две строки встречаться в указанном порядке или они могут быть отменены?

Сценарий 2: переход от родительского контроллера представления к контроллеру родительского представления

[vc willMoveToParentViewController:nil];
[vc.view removeFromSuperview];
[vc removeFromParentViewController];

Также необходимо вызвать [vc didMoveToParentViewController:nil]? Примеры в сеансе 102 не делали этого в этом сценарии, но я не знаю, было ли это упущением или нет.

Сценарий 3: переход от одного родительского контроллера представления к другому

Это, скорее всего, произойдет следующим образом, поскольку логика в каждом контроллере родительского представления будет инкапсулирована.

// In the old parent
[vc willMoveToParentViewController:nil];
[vc.view removeFromSuperview];
[vc removeFromParentViewController];

// In the new parent
[vc willMoveToParentViewController:self];
[self addChildViewController:vc];
[self.view addSubview:vc.view];
[vc didMoveToParentViewController:self];

Вопросы

Мой главный вопрос заключается в следующем: так ли должно работать с контроллером контроллера? Правильны ли приведенные выше механики?

Нужно ли звонить willMoveToParentViewController перед вызовом addChildViewController? Для меня это похоже на логический порядок, но это строго необходимо?

Нужно ли вызывать didMoveToParentViewController:nil после вызова removeFromParentViewController?

Ответ 1

Документы UIViewController довольно понятны, когда и когда не вызывать методы willMove/didMove. Ознакомьтесь с "Реализация контроллера контейнера" .

Документы говорят, что если вы не переопределяете addChildViewController, вам не нужно вызывать метод willMoveToParentViewController:. Однако после завершения перехода вам нужно вызвать метод didMoveToParentViewController:. "Кроме того, диспетчер просмотра контейнера отвечает на вызов метода willMoveToParentViewController: перед вызовом метода removeFromParentViewController. Метод removeFromParentViewController вызывает метод didMoveToParentViewController: контроллера дочернего представления."

Кроме того, здесь приведен пример здесь и пример кода здесь.

Удача

Ответ 2

Эта часть неверна:

[vc willMoveToParentViewController:self];
[self addChildViewController:vc];
[self.view addSubview:vc.view]; // or something like this.
[vc didMoveToParentViewController:self];

Согласно документам:

Когда ваш пользовательский контейнер вызывает метод addChildViewController: он автоматически вызывает метод willMoveToParentViewController: метод контроллера представления, который будет добавлен в качестве дочернего элемента, прежде чем добавлять его.

Таким образом, вам не нужен вызов [vc willMoveToParentViewController:self]. Это делается автоматически, когда вы вызываете [self addChildViewController:vc]. Здесь снова пример кода:

[self addChildViewController:vc];
// [vc willMoveToParentViewController:self] called automatically
[self.view addSubview:vc.view]; // or something like this.
[vc didMoveToParentViewController:self];

Для удаления контроллеров просмотров:

Метод removeFromParentViewController автоматически вызывает метод didMoveToParentViewController: дочернего контроллера просмотра после удаления дочернего элемента.

Предположительно, этот вызов [oldVC didMoveToParentViewController:nil].

[vc willMoveToParentViewController:nil];
[vc.view removeFromSuperview];
[vc removeFromParentViewController];
// [vc didMoveToParentViewController:nil] called automatically