В каких ситуациях в UIKit нужно переопределять метод loadView?

Ответ

Метод loadView() следует переопределять только тогда, когда необходимо полностью программно создать иерархию view для UIViewController, минуя Interface Builder (Storyboard/XIB) или систему по умолчанию.

Типичные случаи использования:

  1. Создание сложного, динамического UI, который неудобно или невозможно описать в Storyboard.
  2. Использование кастомного класса в качестве корневой view контроллера.
  3. Оптимизация производительности для view, которые создаются очень часто, чтобы избежать накладных расходов на загрузку из storyboard.

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

class CustomViewController: UIViewController {

    override func loadView() {
        // 1. Создаём кастомную view
        let customView = UIView()
        customView.backgroundColor = .systemBackground

        // 2. Программно создаём и настраиваем иерархию
        let titleLabel = UILabel()
        titleLabel.text = "Программный интерфейс"
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        customView.addSubview(titleLabel)

        NSLayoutConstraint.activate([
            titleLabel.centerXAnchor.constraint(equalTo: customView.centerXAnchor),
            titleLabel.centerYAnchor.constraint(equalTo: customView.centerYAnchor)
        ])

        // 3. КРИТИЧЕСКИ ВАЖНО: присвоить self.view
        self.view = customView
    }

    // viewDidLoad вызывается после loadView, когда self.view уже создан
    override func viewDidLoad() {
        super.viewDidLoad()
        // Дополнительная настройка
    }
}

Важные правила:

  • Никогда не вызывайте super.loadView() в своей реализации, если создаёте view с нуля.
  • Обязательно присвойте свойство self.view. Если этого не сделать, система вызовет super.loadView() и создаст пустое UIView.
  • Для большинства изменений UI используйте viewDidLoad(). Переопределяйте loadView только для полной замены корневой view.
  • Не используйте loadView для доступа к outlet'ам — в этот момент они ещё не загружены из storyboard.

Ответ 18+ 🔞

А, ну вот, опять про этот loadView() заговорили. Слушай, давай я тебе на пальцах, но с технической точностью, объясню, что это за зверь такой.

Этот метод — твой личный чертёжный стол, на котором ты с нуля собираешь интерфейс, если тебе эти ваши сториборды и ксибы как костыли в жопе. Система тебе говорит: «Ну что, дружок, покажи-ка мне, где тут у тебя self.view?». И если ты в этом методе нихуя не сделал, она сама, вздохнув, создаст тебе пустую белую вьюху. А если сделал — то ты тут царь и бог.

Так когда же в него лезть, спросишь ты? Да когда:

  1. Интерфейс у тебя пиздец какой динамичный и сложный. Такое, что в Interface Builder'е только глаза сломаешь, пытаясь это связать. Типа, там вьюхи плодятся как кролики, в зависимости от данных.
  2. Корневая вьюха у тебя — не UIView, а какой-нибудь твой кастомный SuperDuperChartView. Чтобы система с самого начала знала, с кем имеет дело.
  3. Перформанс — твой бог. Если ты в таблице 500 таких контроллеров создаёшь, а они из сториборда грузятся, то это овердохуища накладных расходов. А тут — хрясь, и готово.

Смотри, как это выглядит в коде, без всякой поебень:

class MyViewController: UIViewController {

    override func loadView() {
        // 1. Всё, начинаем с чистого листа. Никакого super.loadView()! Забудь как страшный сон.
        let mainView = UIView()
        mainView.backgroundColor = .systemGray6 // Ну или какой тебе в душе цвет

        // 2. Лепим всё руками, как из пластилина.
        let button = UIButton(type: .system)
        button.setTitle("Нажми меня, тварь", for: .normal)
        button.translatesAutoresizingMaskIntoConstraints = false
        mainView.addSubview(button)

        // 3. САМОЕ ГЛАВНОЕ, БЛЯДЬ! ЭТОТ ПУНКТ НЕ ПРОЕБИ!
        // Ты должен сказать системе: "Вот, смотри, это теперь моя основная вьюха".
        self.view = mainView

        // 4. И уже потом, на своей свежеиспечённой вьюхе, расставляешь констрейнты.
        NSLayoutConstraint.activate([
            button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        ])
    }

    // А вот viewDidLoad вызовется уже ПОСЛЕ того, как твоя вьюха на месте.
    override func viewDidLoad() {
        super.viewDidLoad()
        // Тут уже можно делать что-то менее фундаментальное. Например, данные подгрузить.
        print("Вьюха загрузилась, можно и выпить")
    }
}

Запомни раз и нахуй:

  • Не зови super.loadView(). Серьёзно, не надо. Ты взял ответственность на себя — вот и тащи её до конца.
  • self.view должен быть назначен. Иначе система посмотрит на тебя как на идиота, вызовет метод родителя сама, и все твои потуги к хуям пойдут.
  • Для 99% обычных дел хватит viewDidLoad(). Туда пихай настройку цвета, текста, загрузку данных. loadView — это для полной замены фундамента.
  • Аутлеты в loadView — это пустое место. Они подтянутся позже, из сториборда. А если ты в loadView, значит, сториборда у тебя и нету. Так что даже не думай про них.

Короче, инструмент мощный, но опасный. Как паяльник в руках обезьяны. Используй с умом, а не потому что «модно — програмно создавать вьюхи».