Всегда ли UIViewController представляет отдельный экран или может быть дочерним элементом
Что проверяют
Знаете ли вы механизм view controller containment, а не только то, что «контроллер равен экрану». Следом почти всегда просят перечислить шаги добавления дочернего контроллера и объяснить, что сломается, если просто добавить его view в иерархию.
Как отвечать
Нет, не всегда. Контроллер отвечает за кусок интерфейса и его жизненный цикл, а экраном этот кусок быть не обязан. Системные контейнеры: UINavigationController, UITabBarController, UISplitViewController, UIPageViewController. Все они держат дочерние контроллеры в children, а у дочернего заполнено свойство parent.
Свой контейнер собирается тремя шагами:
addChild(child): регистрирует связь родителя и ребёнка и заодно вызывает у ребёнкаwillMove(toParent:);view.addSubview(child.view)плюс констрейнты: встраивает интерфейс;child.didMove(toParent: self): этот вызов делаете вы сами, автоматически он не происходит.
Удаление идёт в обратном порядке: willMove(toParent: nil), child.view.removeFromSuperview(), затем removeFromParent(), который сам вызовет didMove(toParent: nil).
Зачем это нужно: после регистрации ребёнок получает viewWillAppear и viewDidAppear вместе с родителем, изменения traitCollection, поворот и безопасные зоны. В сторибордах то же самое даёт Container View с embed-сегвеем.
Практические сценарии: разбить тяжёлый экран на независимые блоки (шапка, список, плеер), переиспользовать один блок на разных экранах, встроить SwiftUI через UIHostingController как дочерний контроллер. Стоит отличать дочерний контроллер от показанного модально: там связь идёт через presentingViewController и presentedViewController, а не через parent.
Где спотыкаются
Добавляют только child.view без addChild. Интерфейс виден, но методы появления не вызываются, трейты не приходят, а сам контроллер может освободиться из памяти, потому что его никто не держит.
Забывают didMove(toParent: self) или путают порядок при удалении, и ребёнок не узнаёт, что его отцепили.
Называют UINavigationController «экраном с кнопкой назад» и не видят в нём такой же контейнер, какой можно написать самому.