2013-06-19 5 views
83

Я просто окунаю ноги в первый раз в развитие iOS, и одна из первых вещей, которые мне пришлось выполнить, - это реализовать custom container view controller - позволяет назвать его SideBarViewController - это своп из какого-либо из нескольких возможных контроллеров детского вида показывает, почти точно как стандартный Контроллер панели бара. (Это в значительной степени Tab Bar Controller но с Hideable стороны меню вместо панели вкладок.)Что на самом деле делает addChildViewController?

В соответствии с инструкциями в документации компании 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 Class Reference есть, в настоящее время:

добавляет указанный контроллер представления, как ребенок. ... Этот метод предназначен только для того, чтобы вызывать реализацию пользовательского контроллера представления контейнера. Если вы переопределите этот метод, вы должны вызвать super в своей реализации.

Там же этот пунктом ранее на ту же странице:

Вашего контейнер-контроллер должен связать контроллер представления ребенка с собой, прежде чем добавить вид корня ребенка в иерархию представлений. Это позволяет iOS правильно маршрутизировать события в контроллеры дочерних представлений и представления, которыми управляют эти контроллеры. Аналогично, после удаления корневого представления ребенка из его иерархии представлений, он должен отключить этот контроллер детского представления от себя. Чтобы создать или разбить эти ассоциации, ваш контейнер вызывает определенные методы, определенные базовым классом. Эти методы не предназначены для вызова клиентами вашего класса контейнеров; они должны использоваться только для реализации вашего контейнера для обеспечения ожидаемого поведения сдерживания.

Вот основные методы, которые вы, возможно, необходимо позвонить:

addChildViewController:
removeFromParentViewController
willMoveToParentViewController:
didMoveToParentViewController:

, но она не дает никакого понятия о том, что «события» или «ожидаемое поведение сдерживания», о которых идет речь, или почему (или даже когда) вызов этих методов является «существенным».

Примеры пользовательских контроллеров контейнеров в разделе «Контроллеры пользовательских контейнеров» в документации Apple полностью вызывают этот метод, поэтому я предполагаю, что он служит важной цели, за исключением того, что он просто выводит дочерний элемент ViewController на массив, но я не может понять, что это за цель. Что делает этот метод, и почему я должен его называть?

+3

компании Apple [** 2011 ** WWDC] (https://developer.apple.com/videos/wwdc/2011/) на странице видео есть страница _great_ («Реализация UIViewController Containment») в этом разделе. – Alladinian

ответ

81

Мне тоже интересно об этом вопросе. Я смотрел Session 102 of the WWDC 2011 видео и г-н View Controller, Bruce D. Nilo, сказал следующее:

viewWillAppear:, viewDidAppear: и т.д. не имеют ничего общего с addChildViewController:. Все, что делает addChildViewController:, заключается в том, чтобы сказать: «Этот контроллер просмотра является дочерним элементом этого», и это не имеет никакого отношения к внешнему виду. Когда они вызываются, это связано с тем, когда представления перемещаются в и из иерархии окон.

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

  • метода Forwarding внешнего вид к контроллерам зрения детей
  • Forwarding метода вращения
  • предупреждение
  • (возможно) пересылка памяти
  • Избежание несогласованных VC иерархий, особенно в transitionFromViewController:toViewController:… где оба VC должны иметь одного и того же родителя
  • Разрешающие пользовательские контроллеры просмотра контейнеров принять участие в государственном сохранении и восстановлении
  • T принимая к участию в цепочке ответчика
  • Hooking вверх navigationController, tabBarController и т.д. свойство
+0

Это сеанс 102 не 101 – SeanChense

+0

+1 для цепи ответчика. addChildViewController требуется, если вы хотите получать события касания на подзаголовке, принадлежащем дочернему элементу UIViewController – charlieb

9

-[UIViewController addChildViewController:] только добавляет переданный в поле зрения контроллер в виде массива viewControllers, на который должен ссылаться viewController (родительский). Вы должны фактически добавить эти представления viewController на экран самостоятельно, добавив их в виде подсмотров другого представления (например, представление parentViewController). В интерфейсе Builder также есть удобный объект для использования childrenViewControllers в Storyboards.

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

Кроме того, childrenViewControllers также автоматически получает все системные события, которые получает родитель: -viewWillAppear, -viewWillDisappear и т. Д. Раньше вы должны были вызывать эти методы вручную на своих «childrenViewControllers».

Все.

+0

Что вы думаете о том, что это все, что он делает? Кроме того, можете ли вы предоставить список «системных событий», которые получат ребенок? Поиск в системе Google системных событий «iOS» не сильно повышается; это не похоже на термин, который использует Apple? –

+0

Это в основном удобный метод, который позволяет вам добавлять представление View Controller B в качестве подзадача View Controller A, но все же иметь View Controller B управлять своим представлением. Чтобы это работало правильно, вам необходимо убедиться, что View Controller B получает системные события (читайте обратные вызовы UIViewControllerDelegate). 'addChildViewController' перехватывает это для вас, чтобы сохранить ваши усилия по передаче всего на ручном. –

94

Я думаю, что пример стоит тысячи слов.

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

enter image description here

Перепробовав несколько решений, я закончил тем, что изобретал свое собственное решение, чтобы показать блокнот. Поэтому, когда я хочу показать блокнот, я создаю новый экземпляр NotepadViewController и добавлю его корневой вид в качестве подсмотра к основному виду. Все идет нормально.

Тогда я заметил, что изображение блокнота частично скрыто под клавиатурой в ландшафтном режиме.

enter image description here

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

Чтобы решить эту проблему, мне нужно было вызвать все методы из NotepadViewController вручную, когда они вызывают в главном контроллере. Это скоро усложнит ситуацию и создаст дополнительную зависимость между несвязанными компонентами приложения.

Это было в прошлом, прежде чем вводится концепция контроллеров детского вида. Но теперь вам нужно только до addChildViewController к главному контроллеру просмотра, и все будет работать так, как ожидалось, без какой-либо ручной работы.

Edit: Есть две категории событий, которые пересылаются к контроллерам зрения ребенка:

1- Внешний вид Методы:

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

2- Методы вращения:

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

Вы также можете управлять тем, какие категории событий вы хотите переслать автоматически, переопределяя shouldAutomaticallyForwardRotationMethods и shouldAutomaticallyForwardAppearanceMethods.

+0

Из документации и после проведения быстрого теста я не думаю, что есть другое событие, которое отправляется только в том случае, если вы добавили 'addChildViewController' в родительский контроллер. – Hejazi

+0

Желательно, чтобы он автоматически пересылал viewWillLayoutSubviews – MobileMon