Когда вызывается метод layoutSubviews() у UIView?

«Когда вызывается метод layoutSubviews() у UIView?» — вопрос из категории UIKit, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Система автоматически вызывает layoutSubviews(), когда требуется обновить геометрию (layout) всех подвью (subviews). Это ключевой метод для ручной верстки.

Основные триггеры вызова:

  1. Изменение bounds (размера) самого view. Например, при повороте устройства или изменении frame/bounds.
  2. Добавление или удаление подвью с помощью addSubview(_:) или removeFromSuperview().
  3. Вызов setNeedsLayout() на view. Этот метод помечает view как нуждающееся в обновлении layout'а, и вызов layoutSubviews() произойдет в следующем цикле run loop.
  4. Вызов layoutIfNeeded() на view. Если для view был вызван setNeedsLayout(), этот метод принудительно вызывает layoutSubviews() немедленно, синхронно. Используется для анимации изменений layout'а.
  5. Прокрутка UIScrollView (так как меняется bounds скроллящегося контента).
  6. Изменение safeAreaInsets (например, появление/скрытие navigation bar).

Практический пример:

class CustomView: UIView {
    let label = UILabel()

    override init(frame: CGRect) {
        super.init(frame: frame)
        addSubview(label)
    }

    override func layoutSubviews() {
        super.layoutSubviews() // Всегда вызывайте super
        // Здесь вычисляем и задаем фреймы подвью на основе текущих bounds
        label.frame = bounds.insetBy(dx: 16, dy: 8)
    }
}

// Использование:
let view = CustomView(frame: CGRect(x: 0, y: 0, width: 200, height: 100))
view.setNeedsLayout() // Запланирует вызов layoutSubviews
view.layoutIfNeeded() // Вызовет layoutSubviews немедленно (если нужно)

// Изменение размера вызовет layoutSubviews автоматически
view.frame.size = CGSize(width: 300, height: 150)

Важно: В layoutSubviews() следует только располагать подвью, а не добавлять/удалять их. Для добавления/удаления лучше использовать init или другие методы жизненного цикла.