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 | Обмежена через 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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.