Жизненный цикл ´UIViewController´

Жизненный цикл ´UIViewController´

Рассмотрим когда вызываются методы контроллера и что можно делать внутри них. Когда настраивать вьюхи и данные.


Имя Автора
Автор Иван Воробей iOS разработчик. Пишу библиотеки, веду телеграм-канал.

Содержание


Если вы вызываете инициализатор уUIView - система сразу выделит память. У контроллера тоже есть вью, но

Система ждет причину создать вью, мы разберем как это работает. Концепция жизненного цикла строится вокруг этой особенности. Просто держите в уме, что вью создается по необходимости.


Инициализируем

Рассмотрим базовый UIViewController, инициализаторов два:

override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
    
required init?(coder: NSCoder) {
    super.init(coder: coder)
}

Ещё есть инициализатор без параметров init(), но это обертка над первым инициализатором.

На этом этапе контроллер инициализирует проперти и отрабатывает тело инициализатора. Вью не загружается, аутлеты не активны. В инициализаторе с nib сохраняется только имя файла, сам файл не подгружается. Подробнее про загрузку вью дальше.


Загружаем

Разработчик презентует контроллер. Для системы это повод выделить память и загрузить вью. Мы можем следить за процессом и даже вмешаться. Глянем какие методы доступны:

override func loadView() {}

Метод loadView() вызывается системой. Его не нужно вызывать вручную, но можно переопределить, чтобы подменить корневую вью. Если нужно загрузить вью вручную (и вы знаете что делаете), то держите красную кнопку loadViewIfNeeded(). Узнать или загружена вью можно через проперти контроллера isViewLoaded.

Второй метод легендарен, как Стив Джобс. Он вызывается когда вью закончила загрузку.

override viewDidLoad() {
    super.viewDidLoad()
}

Разработчики не просто так делают настройку контроллера и вьюх в методе viewDidLoad(). До вызова этого метода корневая вью еще не существует, а после контроллер уже готов появиться на экране. viewDidLoad() - отличное место. Память под вью выделена, вью загружена и готова к настройке.

Проект от такого не крашнется, но элементы интерфейса расходуют память - нет смысла тратить её раньше, чем нужно. Лучше делать это по необходимости.

Раньше я делал проперти-вьюхи контроллера просто создавая их:

class ViewController: UIViewController {
    
    let redView = UIView()
}

Проперти инициализируется вместе с контроллером, а значит память для вью выделится сразу. Чтобы отложить это до требования, нужно пометить проперти как lazy.

В методе viewDidLoad() размеры вьюхи неверные, привязываться к высоте и ширине нельзя. Делайте настройку, которая не зависят от размеров.

Хочу остановиться на viewDidUnload(). Корневая вью может выгружаться из памяти, а это означает кое-что невероятное:

Если модальный контроллер закрыть, вью выгрузится из памяти, но объект контроллера будет жив. Аутлеты здесь активны, но уже не имеют смысла - их можно ресетить. Если показать контроллер еще раз - вью снова загрузится. Если система выгрузила вью, значит была причина. Не нужно обращаться к корневой вью в этом методе - это загрузит вью.

Не нужно срочно брать внеурочные и все выходные переделывать вашу VPN-ку. Ничего не сломается, viewDidLoad() редко вызывается несколько раз. Держите в уме, что нужно разнести настройку данных и вьюх в следующем проекте.


Показываем и прячем

Появление контроллера начинается с метода viewWillAppear:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
}
    
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
}

Появление контроллера в модальном окне, или переход в UINavigationController-e вызовут viewWillAppear до анимации, а viewDidAppear — после. При вызове viewWillAppear, вью уже находится в иерархии.

Оба метода в связке. Тут делать настройку не нужно, но можно спрятать/показать вьюхи или добавить несложное поведение. В методе viewDidAppear() начинайте сетевой запрос или крутите индикатор загрузки. Оба метода могут вызываться несколько раз.

Есть методы, которые сообщают что вью пропадает с экрана. Наглядная схема:

Схема жизненного цикла `ViewController`.

Обратите внимание на пару антагонистов viewWillDisappear() и viewDidDisappear(). Они вызываются, когда вью удаляется из иерархии представлений. Если вы показываете другой контроллер поверх, то методы не вызываются.


Layout

Методы лейаута, аналогично методам выше, подвязаны к жизненному циклу вьюхи. Доступно 3 метода:

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
}
    
override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
}

Первый метод вызывается до layoutSubviews() корневой вью, второй - после. Во втором методе размеры корректные, а вью размещены правильно - можно подвязываться к размерам корневой вью.

Есть отдельный метод про изменение размеров вью. Это не обязательно поворот устройства, хотя он тоже:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
}

После будут вызваны методы viewWillLayoutSubviews() и viewDidLayoutSubviews().


Кончилась память

Если вы не очистите объекты, из-за которых это происходит, iOS принудительно крашнет приложение.

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

Другие туториалы

Уровни доступа в Swift

Рассмотрим уровни доступа и как обезопасить свой код с ними.

Как добавить кастомную SwiftUI View в библиотеку Xcode

В этой статье я покажу как добавить свою View в Xcode Library с помощью LibraryContentProvider.

Действия на сочетания клавиш в SwiftUI

Знакомимся с модификатором keyboardShortcut. Добавим модификаторы для клавиш .command, .option, .shift

´UISheetPresentationController´ как в приложении Карты

В iOS 15 появились sheet-контроллеры. Их можно перетаскивать с изменением высоты. Вы встречали эти контроллеры в приложениях «Карты» и «Акции».

В telegram-канале приходят уведомления о новых туториалах. В чате для iOS разработчиков ответят на вопросы.

Открыть Telegram-канал