viewDidLoad — это первый метод, который UIKit вызывает после загрузки View UIViewController в память. По данным Apple Developer Documentation, этот метод вызывается ровно один раз за всё время существования контроллера. viewDidLoad — основное место для начальной настройки интерфейса, регистрации ячеек и инициализации данных.
Главное
viewDidLoad — это метод экземпляра UIViewController, который UIKit вызывает сразу после того, как View контроллера была загружена в оперативную память. К этому моменту все IBOutlet-свойства уже соединены с элементами интерфейса, но View ещё не добавлена в иерарсию окон и не видна пользователю. Разработчик переопределяет этот метод, чтобы выполнить начальную конфигурацию экрана.
Метод является частью ViewController Lifecycle и следует сразу после loadView, если View создаётся программно, или после загрузки из Storyboard. В типичном проекте viewDidLoad — самый часто переопределяемый метод UIViewController, так как он предоставляет безопасную точку для работы с subviews, которые уже существуют и готовы к настройке.
Важная деталь: к моменту вызова viewDidLoad размеры View ещё не соответствуют финальным — Auto Layout не завершил проходов, и frame может отличаться от ожидаемого. Для расчётов, зависящих от размеров, используют viewDidLayoutSubviews.
Тайминг вызова viewDidLoad зависит от того, как инициализируется контроллер. В большинстве случаев UIKit вызывает этот метод автоматически при первом обращении к свойству view контроллера — это называется lazy-loading механизмом UIViewController.
Когда NavigationController или TabBarController впервые показывает ваш экран, UIKit проверяет, загружена ли View. Если нет — вызывается loadView (или загрузка из Storyboard), после чего немедленно срабатывает viewDidLoad. Это стандартный сценарий, и он происходит однократно для каждого экземпляра контроллера.
override func viewDidLoad() {
super.viewDidLoad()
print("View загружена — можно настраивать интерфейс")
setupUI()
configureTableView()
}
viewDidLoad не вызывается повторно при возврате на экран через back button или dismiss. Если ваша логика зависит от того, что экран появляется снова — размещайте её в viewWillAppear. Это одна из самых частых концептуальных ошибок: разработчики ожидают, что viewDidLoad сработает при каждом показе, но UIKit вызывает его всего один раз.
Иногда разработчики принудительно вызывают view у контроллера, чтобы инициировать загрузку заранее: let _ = controller.view. Это форсирует вызов loadView и viewDidLoad до того, как контроллер появится на экране. Такой приём используют, когда нужно подготовить View заранее для плавного перехода.
viewDidLoad предназначен для одноразовых операций настройки, которые не зависят от того, видим ли экран. Правильное использование этого метода — залог чистой архитектуры и предсказуемого поведения контроллера.
В viewDidLoad регистрируют nib-файлы и классы для UITableView и UICollectionView, настраивают делегатов, задают первоначальные значения свойств UI-элементов. Поскольку все IBOutlet к этому моменту уже связаны, можно безопасно обращаться к label.text, imageView.image и другим свойствам subviews.
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
tableView.register(
CustomCell.self,
forCellReuseIdentifier: CustomCell.identifier
)
title = "Главный экран"
}
Здесь создают viewModel, инициализируют data source массивами, подписываются на уведомления, которые должны действовать всё время жизни контроллера. Например, подписка на UIApplication.willEnterForegroundNotification для обновления данных при возврате из фона — правильный кандидат для viewDidLoad. ViewModel в современной iOS-архитектуре выступает связующим звеном между контроллером и бизнес-логикой, и её инициализация именно в viewDidLoad обеспечивает готовность данных к моменту первого появления экрана.
Особое внимание уделите настройке data source для таблиц и коллекций. Если ваша таблица использует UIFetchedResultsController или NSFetchedResultsController с Core Data, инициализируйте fetch request и делегат в viewDidLoad. Это гарантирует, что при первом появлении экрана таблица уже будет заполнена данными без дополнительных запросов.
В viewDidLoad конфигурируют кнопки NavigationBar, настраивают large title, добавляют search controller и устанавливают кнопки edit/done. Эти элементы редко меняются при повторных показах экрана, поэтому их инициализация здесь оптимальна.
Не все операции уместны в viewDidLoad. Некоторые действия, помещённые в этот метод, приводят к избыточному потреблению памяти, неправильному поведению или багам при повторных показах экрана.
Избегайте запуска сетевых запросов, результат которых влияет только на UI. Если запрос завершится до появления экрана, пользователь не увидит результат, а если после — данные могут устареть. Инициируйте загрузку в viewDidLoad, но обновляйте UI в viewWillAppear.
Не делайте в viewDidLoad операций, зависимых от размеров и положения View. На момент вызова Auto Layout не завершил проходы, и frame может быть нефинальным. Для расчётов используйте viewDidLayoutSubviews или переопределите updateViewConstraints.
Не подписывайтесь на уведомления, которые действуют только когда экран видим. Клавиатурные уведомления, уведомления об изменении контента дочерних контроллеров — подписывайтесь на них в viewWillAppear и отписывайтесь в viewDidDisappear, чтобы избежать лишних вызовов и утечек.
Не вызывайте методы, требующие видимого экрана. Например, попытка показать UIAlertController из viewDidLoad приведёт к ошибке, так как View контроллера ещё не добавлена в иерархию окон. Любые UI-операции, зависящие от window или presentedViewController, должны выполняться только после появления экрана.
Не инициализируйте тяжёлые ресурсы без необходимости. Если на экран приходят редко или данные отображаются не сразу, отложите создание ресурсоёмких объектов до момента, когда они действительно понадобятся. Lazy-инициализация свойств в Swift — встроенный механизм для решения этой задачи: свойство с модификатором lazy будет создано только при первом обращении к нему, что экономит память и ускоряет загрузку экрана.
Не используйте viewDidLoad для операций, которые должны выполняться при каждом появлении экрана. Это самая фундаментальная ошибка: начинающие разработчики часто размещают логику обновления данных в viewDidLoad и удивляются, что при возврате с другого экрана таблица не перезагружается. Если операция должна повторяться при каждом показе — используйте viewWillAppear. Если должна выполниться один раз за время жизни — viewDidLoad. Запомните это простое правило, чтобы избежать большинства проблем с жизненным циклом UIViewController.
Рассмотрим три практических примера, демонстрирующих правильное использование viewDidLoad в реальных проектах. Каждый пример решает конкретную задачу настройки экрана.
override func viewDidLoad() {
super.viewDidLoad()
collectionView.register(
PhotoCell.self,
forCellWithReuseIdentifier: PhotoCell.reuseId
)
collectionView.register(
HeaderView.self,
forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
withReuseIdentifier: HeaderView.reuseId
)
viewModel.delegate = self
viewModel.fetchInitialPage()
}
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel()
label.text = "Привет, мир!"
label.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
label.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
В viewDidLoad также настраивают элементы, отображаемые при отсутствии данных: пустое состояние, лоадер, placeholder. Эти компоненты создаются один раз и переиспользуются при каждом появлении экрана. Сокрытие или показ этих элементов управляется в viewWillAppear в зависимости от актуальных данных.
override func viewDidLoad() {
super.viewDidLoad()
emptyStateLabel = UILabel()
emptyStateLabel.text = "Нет данных"
emptyStateLabel.textAlignment = .center
emptyStateLabel.isHidden = true
view.addSubview(emptyStateLabel)
activityIndicator = UIActivityIndicatorView(style: .medium)
activityIndicator.hidesWhenStopped = true
view.addSubview(activityIndicator)
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(handleEnterForeground),
name: UIApplication.willEnterForegroundNotification,
object: nil
)
}
@objc private func handleEnterForeground() {
refreshContent()
}
Часто задаваемые вопросы
В нормальных условиях нет — UIKit вызывает viewDidLoad один раз после загрузки View в память. Если контроллер уничтожается и создаётся заново, viewDidLoad сработает для нового экземпляра.
Да, обязательно. Вызов super.viewDidLoad гарантирует, что UIKit выполнит внутреннюю настройку, необходимую для корректной работы Lifecycle. Всегда вызывайте super первым делом внутри метода.
viewDidLoad вызывается один раз при загрузке View. viewWillAppear вызывается каждый раз перед появлением экрана. Первый — для одноразовой настройки, второй — для обновления данных и состояния.
Тяжёлые синхронные операции в viewDidLoad блокируют main thread и задерживают появление экрана. Асинхронные загрузки допустимы, но обновлять UI по их завершению нужно с учётом, что экран может быть уже скрыт.
Напрямую viewDidLoad вызвать нельзя — его вызывает UIKit. Чтобы форсировать загрузку View, обратитесь к свойству controller.view. Это запустит loadView и viewDidLoad автоматически.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также