Как и когда происходит инициализация свойства `view` у UIViewController?

Ответ

Свойство view инициализируется лениво в момент первого обращения к нему (геттеру). Этот процесс называется загрузкой представления.

Жизненный цикл загрузки view:

  1. Запрос view: Код (система или разработчик) впервые обращается к self.view.
  2. Вызов loadView(): Если view равно nil, система вызывает этот метод.
  3. Создание view в loadView():
    • По умолчанию: Метод создает пустой объект UIView.
    • Из Storyboard/XIB: Метод загружает иерархию из файла.
    • Кастомная реализация: Вы можете переопределить loadView(), чтобы создать представление вручную.
  4. Присвоение: Созданное представление присваивается свойству view.
  5. Вызов viewDidLoad(): Сразу после загрузки. Идеальное место для начальной настройки.

Важные нюансы:

  • Не вызывайте super.loadView() при ручном создании view, иначе вы создадите два представления.
  • Все outlet'ы гарантированно установлены к моменту вызова viewDidLoad().
  • Используйте viewIfLoaded для безопасной проверки, загружено ли представление.

Пример переопределения loadView():

class CustomViewController: UIViewController {
    override func loadView() {
        // 1. Создаем кастомное view вручную
        let customView = CustomView(frame: UIScreen.main.bounds)
        customView.backgroundColor = .systemBackground
        // 2. НЕ вызываем super.loadView()
        // 3. Присваиваем свойству view
        self.view = customView
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Теперь self.view — это наш CustomView
        print("View is loaded: (type(of: self.view))") // CustomView
    }
}

Ответ 18+ 🔞

А, ну вот, смотри, опять про эту ленивую загрузку view пошли разговоры. Это ж классика, блядь, как Герасим с Муму, только в мире iOS. Представь: твой контроллер — это такой вот немой здоровяк Герасим, а его view — это та самая собачка Муму. Пока ты её не позовёшь, её нихуя нет!

Как это работает, ёпта:

  1. Ты зовёшь Муму: Твой код впервые лезет к self.view. Типа «Му-му! Где ты, сука?».
  2. Система вспоминает, что надо её создать: Ага, viewnil. Значит, пора вызывать священный метод loadView(). Это как Герасим пошёл искать щенка.
  3. Создание Муму в loadView():
    • Если ты ленивая жопа и ничего не делал: Система сама создаст пустую UIView. Как будто нашёл какую-то дворняжку.
    • Если ты в сториборде накидал: Система аккуратно достанет и соберёт всю твою красоту из файла. Вот это уже породистая Муму.
    • Если ты умник и переопределил метод: Ты сам, своими кривыми ручками, создаёшь вьюху в коде. Сам решаешь, какой будет твоя Муму.
  4. Присвоение: Созданную вьюху запихивают в свойство view. Всё, Муму теперь живет у Герасима в будке.
  5. Праздник: Сразу после этого вызывается viewDidLoad(). Это момент, когда Герасим уже привёл Муму домой, погладил и думает: «Ну вот, теперь можно и миску поставить, и ошейник купить». Идеальное место для начальной настройки.

А теперь, блядь, важные моменты, чтобы не вышло как у Тургенева:

  • Не вызывай super.loadView() в своей кастомной реализации! Это пиздец. Это как если бы Герасим, создав свою Муму, позвал бы ещё и собаковода из деревни — получишь две собаки, которые начнут сраться за миску. Одну ты создал, вторую создаст система — и будет тебе овердохуища проблем.
  • К моменту viewDidLoad() все аутлеты уже проинициализированы. Можно не бздеть, что что-то nil.
  • Хочешь безопасно проверить, жива ли ещё твоя Муму (загружена ли вьюха), не вызывая её создание? Используй viewIfLoaded. Это как тихонько заглянуть в будку, а не орать «МУМУ!» на всю деревню.

Вот смотри, как это выглядит в коде, если ты решил быть режиссёром и сам создать свою Муму:

class CustomViewController: UIViewController {
    override func loadView() {
        // 1. Вручную лепим свою кастомную вьюху
        let customView = CustomView(frame: UIScreen.main.bounds)
        customView.backgroundColor = .systemBackground
        // 2. БЛЯДЬ, НЕ ВЫЗЫВАЕМ super.loadView()! Запомни как «Отче наш»!
        // 3. Говорим системе: вот, держи, это теперь моё главное view
        self.view = customView
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Теперь self.view — это наша рукотворная CustomView
        print("View is loaded: (type(of: self.view))") // Напечатает: CustomView
    }
}

Вот и вся философия. Главное — не перемудрить, а то утопишь свою вьюху, как Герасим собачку, и потом будешь метаться по коду с криками «МУМУ!», а она уже на дне.