UIViewController — центральный класс iOS-приложения, управляющий экраном и его содержимым. Каждый экран iPhone или iPad управляется одним ViewController, который координирует отображение, жизненный цикл и навигацию. Подробнее об архитектуре UIKit читайте в официальной документации Apple.
Главное
UIViewController — класс из фреймворка UIKit, который управляет иерархией UIView и координирует отображение данных на экране. Каждое приложение iOS содержит минимум один ViewController — корневой контроллер окна. Контроллер обрабатывает повороты экрана, переходы между экранами и события жизненного цикла.
Архитектура MVC (Model-View-Controller) в iOS реализована именно через UIViewController: контроллер получает данные от модели и обновляет представление. ViewController не является визуальным элементом — он управляет свойством view, которое содержит иерархию subview. По данным Apple (2026), UIKit содержит более 40 встроенных подклассов UIViewController.
Первый iPhone SDK (2008) включал UIViewController с тремя методами жизненного цикла. За 18 лет Apple добавила поддержку Container View Controller, адаптивные презентации, UIViewControllerTransitioningDelegate для кастомных анимаций и режим разделённого экрана на iPad. UIViewController остаётся обязательным компонентом для приложений на UIKit.
Жизненный цикл UIViewController — последовательность методов, вызываемых системой при создании, отображении и скрытии экрана. Понимание Lifecycle критически важно: неправильное размещение кода приводит к утечкам памяти, лишним сетевым запросам и мерцанию интерфейса.
| Метод | Момент вызова | Назначение |
|---|---|---|
| viewDidLoad | Один раз, после загрузки view в память | Начальная настройка UI, подписка на Combine |
| viewWillAppear | Перед появлением экрана | Обновление данных, скрытие/показ navigation bar |
| viewDidAppear | После появления экрана | Запуск анимаций, аналитика, обновление камеры |
| viewWillDisappear | Перед уходом с экрана | Сохранение черновиков, отписка от уведомлений |
| viewDidDisappear | После ухода с экрана | Остановка тяжёлых процессов, освобождение ресурсов |
При первом показе экрана последовательность: init → loadView → viewDidLoad → viewWillAppear → viewDidAppear. При повторном появлении (возврат с другого экрана): viewWillAppear → viewDidAppear. viewDidLoad вызывается только один раз за время жизни контроллера.
Метод viewDidLoad — основная точка настройки пользовательского интерфейса. Вызывается после загрузки view в память, когда все IBOutlet уже связаны. Здесь создаются элементы UI программно, настраиваются constraints, загружаются начальные данные.
final class ProfileViewController: UIViewController {
private let tableView = UITableView()
private let viewModel = ProfileViewModel()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
bindViewModel()
}
private func setupUI() {
view.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
tableView.register(ProfileCell.self,
forCellReuseIdentifier: ProfileCell.reuseId)
}
private func bindViewModel() {
viewModel.$user
.receive(on: DispatchQueue.main)
.sink { [weak self] user in
self?.title = user.name
}
.store(in: &cancellables)
}
}В SwiftUI этот код эквивалентен телу View. Но UIViewController даёт полный контроль над жизненным циклом и оптимизацией. bindViewModel использует Combine для реактивной подписки — данные обновляются автоматически при изменении модели.
viewWillAppear вызывается каждый раз перед появлением экрана, даже если он уже был в памяти. Это место для обновления данных, которые могли измениться на другом экране: перезагрузка списка, обновление счётчика уведомлений, настройка navigation bar под конкретный экран.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Скрываем navigation bar на этом экране
navigationController?.setNavigationBarHidden(true, animated: animated)
// Обновляем данные при возврате с другого экрана
tableView.reloadData()
badgeLabel.text = "\(CartManager.shared.itemCount)"
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Аналитика: только после того, как пользователь увидел экран
AnalyticsService.shared.logScreenView("Profile")
}Разница между viewDidLoad и viewWillAppear критична: viewDidLoad выполняется один раз и подходит для статичной настройки, viewWillAppear — каждый раз при показе, подходит для динамических обновлений. Помещение сетевых запросов в viewDidLoad приведёт к показу устаревших данных при возврате на экран.
Container View Controller — ViewController, который управляет одним или несколькими дочерними ViewController. Apple предоставляет три встроенных контейнера: UINavigationController (стек экранов), UITabBarController (вкладки) и UISplitViewController (мастер-детейл для iPad).
UINavigationController организует переходы в стек — push добавляет экран, pop удаляет. UITabBarController переключает между независимыми разделами приложения. UISplitViewController показывает два контроллера рядом на iPad и один на iPhone. Разработчик может создать собственный контейнер через addChild.
// Кастомный Container View Controller
final class ContainerViewController: UIViewController {
private let sidebarVC = SidebarViewController()
private let contentVC = ContentViewController()
override func viewDidLoad() {
super.viewDidLoad()
// Добавление дочернего контроллера
addChild(sidebarVC)
view.addSubview(sidebarVC.view)
sidebarVC.didMove(toParent: self)
addChild(contentVC)
view.addSubview(contentVC.view)
contentVC.didMove(toParent: self)
}
}Правильная работа с Container View Controller требует вызова addChild, добавления view и didMove(toParent:) в этом порядке. При удалении — willMove(toParent: nil), removeFromSuperview, removeFromParent. Нарушение последовательности приводит к утечкам памяти.
Проблема Massive View Controller — когда UIViewController содержит сотни строк кода с бизнес-логикой, сетевыми запросами, навигацией и UI-кодом. Apple осознаёт проблему и рекомендует MVVM (Model-View-ViewModel) вместе с Coordinator для выноса навигации.
MVVM переносит бизнес-логику из контроллера в ViewModel. Controller только связывает ViewModel с View через Combine или делегат. Coordinator выносит навигационную логику — создание и переход между контроллерами — в отдельный класс. Этот подход внедрён в лучшие практики Apple с 2024 года.
// Coordinator — управление навигацией
protocol Coordinator {
var childCoordinators: [Coordinator] { get set }
func start()
}
final class MainCoordinator: Coordinator {
var childCoordinators = [Coordinator]()
private let navigationController: UINavigationController
init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
func start() {
let vc = ListViewController()
vc.didSelectItem = { [weak self] item in
self?.showDetail(item)
}
navigationController.pushViewController(vc, animated: false)
}
private func showDetail(_ item: Item) {
let vc = DetailViewController(item: item)
navigationController.pushViewController(vc, animated: true)
}
}Выбор между UIViewController и SwiftUI View зависит от года начала проекта, требований к кастомизации и минимальной поддерживаемой версии iOS. UIKit с UIViewController остаётся основой для проектов, начатых до 2020 года, и для приложений с глубокой кастомизацией интерфейса.
SwiftUI подходит для новых проектов с iOS 17+, стандартных интерфейсов и прототипов. Однако для кастомных переходов, работы с камерой, MapKit, сложной анимации CALayer требуется UIViewController. Apple рекомендует сочетать подходы через UIHostingController (SwiftUI внутри UIKit) и UIViewRepresentable (UIKit внутри SwiftUI).
| Сценарий | UIKit (UIViewController) | SwiftUI (View) |
|---|---|---|
| Кастомная анимация | Полный контроль через UIViewPropertyAnimator | Ограниченная through Animation |
| Работа с камерой | AVCaptureSession + UIViewPreview | Через UIViewControllerRepresentable |
| CollectionView | UICollectionView + UICollectionViewLayout | LazyVGrid/LazyHGrid |
| Адаптация iPad | UISplitViewController + UITraitCollection | NavigationSplitView + sizeClass |
| Скорость разработки | Медленнее (ручной layout) | Быстрее (декларативный) |
Часто задаваемые вопросы
UIViewController — контроллер, управляющий экраном и его жизненным циклом. UIView — представление, отображающее контент. ViewController содержит иерархию UIView, но сам не является визуальным элементом. Один контроллер управляет множеством вью.
Massive View Controller — антипаттерн, когда UIViewController содержит слишком много логики: данные, навигацию, сетевые запросы, анимацию. Решение — вынос кода в отдельные сервисы, координаторы и ViewModel (MVVM).
Четыре способа: через свойство при prepare(for:sender:) (Segue), через делегат (Delegate), через замыкание (Closure), через общий сервис. Для слабой связанности используют Coordinator + Delegate или Combine.
Container View Controller — контроллер, управляющий дочерними ViewController. Примеры: UINavigationController, UITabBarController, UISplitViewController. Родительский контроллер добавляет дочерние через addChild, переключается между ними и управляет их layout.
UIViewController — для сложной кастомной анимации, работы с камерой, картой, видео, UICollectionView с кастомным layout. SwiftUI View — для стандартных интерфейсов iOS 13+. Допустимо сочетание через UIHostingController.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.