Как представить структуру контроллеров и сабвью как структуру данных в iOS?
Что проверяют
Видите ли вы, что иерархия экранов в UIKit это дерево, и знаете ли свойства, через которые по нему ходят. Следом обычно просят написать обход: найти все подвью нужного типа или верхний видимый контроллер.
Как отвечать
Назовите два дерева и их связи:
- иерархия вью: у
UIViewестьsuperviewи упорядоченный массивsubviews. Порядок задаёт наложение, последний элемент рисуется поверх остальных. Корень дереваUIWindow; - иерархия контроллеров: у
UIViewControllerестьparentиchildren. Дочерний контроллер добавляют черезaddChild(_:), затем вставляют егоview, затем вызываютdidMove(toParent:). Контейнеры хранят детей по-своему: уUINavigationControllerстекviewControllers, уUITabBarControllerмассив иselectedViewController; - модальные экраны:
presentedViewControllerиpresentingViewControllerобразуют отдельную цепочку, вchildrenони не попадают.
Как структура данных это n-арное дерево, и задачи на нём решаются обходом в глубину:
extension UIView {
func allSubviews<T: UIView>(of type: T.Type) -> [T] {
subviews.compactMap { $0 as? T } + subviews.flatMap { $0.allSubviews(of: type) }
}
}Верхний контроллер ищут так же рекурсивно: сначала presentedViewController, затем видимый экран навигации или выбранная вкладка.
Если нужно своё дерево, например для снапшот-тестов или логов, узел хранит детей массивом, а ссылку на родителя делает weak, иначе получится цикл удержания. UIKit устроен так же: родитель держит детей сильной ссылкой, обратная связь слабая. Для отладки назовите Debug View Hierarchy в Xcode.
Где спотыкаются
Уходят в пересказ MVC и MVVM. Вопрос про структуру данных, и ждут слово «дерево», свойства связей и обход.
Добавляют дочерний контроллер одним addSubview, без addChild(_:) и didMove(toParent:). Вью на экране, но контроллер не получает от родителя viewWillAppear и изменения трейтов.
Путают модальный показ с вложением. present(_:animated:) не делает экран ребёнком, и обход только по children его не найдёт.