UIKitJuniorCoding

Что такое Auto Layout и как создавать constraints программно по сравнению с Interface Builder?

Auto Layout вычисляет позицию и размер view через математические constraints. Программно используйте NSLayoutAnchor + NSLayoutConstraint.activate([…]), всегда устанавливая translatesAutoresizingMaskIntoConstraints = false.

Auto Layout — это система ограничений (constraints) в UIKit, которая вычисляет размер и позицию каждого view на основе набора математических соотношений. Вместо того чтобы задавать фиксированный frame, вы описываете отношения между атрибутами: левый край этого view = правый край того view + 8pt.

Ключевые классы и атрибуты

  • NSLayoutConstraint — базовый класс одного ограничения.
  • NSLayoutAnchor и его подклассы: NSLayoutXAxisAnchor, NSLayoutYAxisAnchor, NSLayoutDimension — современный API (iOS 9+).
  • UILayoutGuide — невидимый прямоугольник для группировки ограничений без лишних view.
  • translatesAutoresizingMaskIntoConstraints — обязательно false для любого view, которое вы добавляете программно.

Программное создание constraints через NSLayoutAnchor

import UIKit

class CardViewController: UIViewController {

    private let cardView: UIView = {
        let v = UIView()
        v.backgroundColor = .systemBackground
        v.layer.cornerRadius = 12
        v.translatesAutoresizingMaskIntoConstraints = false  // обязательно
        return v
    }()

    private let titleLabel: UILabel = {
        let l = UILabel()
        l.font = .systemFont(ofSize: 17, weight: .semibold)
        l.numberOfLines = 0
        l.translatesAutoresizingMaskIntoConstraints = false
        return l
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(cardView)
        cardView.addSubview(titleLabel)
        setupConstraints()
    }

    private func setupConstraints() {
        NSLayoutConstraint.activate([
            // cardView: прижат к safe area с отступами 16pt
            cardView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16),
            cardView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
            cardView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),

            // titleLabel внутри cardView с padding 12pt
            titleLabel.topAnchor.constraint(equalTo: cardView.topAnchor, constant: 12),
            titleLabel.leadingAnchor.constraint(equalTo: cardView.leadingAnchor, constant: 12),
            titleLabel.trailingAnchor.constraint(equalTo: cardView.trailingAnchor, constant: -12),
            titleLabel.bottomAnchor.constraint(equalTo: cardView.bottomAnchor, constant: -12),
        ])
    }
}

Сравнение с Interface Builder

  • Interface Builder (Storyboard / XIB): constraints создаются мышью, хранятся в XML. Удобно для статичных экранов, но при конфликтах («ambiguous layout») IB показывает предупреждения жёлтым. Merge-конфликты в storyboard — боль при командной работе.
  • Программно: полный контроль, легко переиспользовать через функции/подклассы, diff в git читаем. Но больше кода и нет визуального превью без запуска Xcode Preview / симулятора.
  • NSLayoutConstraint.activate([…]): активировать нужно массивом — это эффективнее, чем вызывать .isActive = true по одному (один проход по движку Auto Layout вместо N).

Content Hugging и Compression Resistance

Два приоритета, о которых часто забывают:

  • contentHuggingPriority — насколько view сопротивляется растяжению сверх intrinsicContentSize. По умолчанию 250.
  • compressionResistancePriority — насколько view сопротивляется сжатию. По умолчанию 750.
// Иконка не должна растягиваться — поднимаем hugging
iconImageView.setContentHuggingPriority(.required, for: .horizontal)
// Заголовок может сжиматься меньше, чем подзаголовок
titleLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)

Подводные камни

  • Забыть translatesAutoresizingMaskIntoConstraints = false — view получит автоматически сгенерированные constraints, которые будут конфликтовать с вашими.
  • Добавлять constraints до того, как view добавлен в иерархию (addSubview) — runtime crash с неясным сообщением об ошибке.
  • Использовать constraint.isActive = true в цикле вместо NSLayoutConstraint.activate([…]) — избыточные проходы движка, особенно заметно при большом числе constraints.
  • Не учитывать safeAreaLayoutGuide — контент уходит под notch или home indicator на современных iPhone.
  • Держать ссылки на constraints для последующего изменения через constraint.constant = …, а не деактивировать-активировать их попарно — деактивация/активация дороже изменения constant.
  • Ambiguous layout в runtime — используйте view.hasAmbiguousLayout и view.exerciseAmbiguityInLayout() в Debug-сборке для диагностики.
  • Смешивать Auto Layout и прямое задание frame в одном view — непредсказуемые результаты после следующего layout-прохода.
  • Не тестировать на Dynamic Type и разных размерах экрана — фиксированные высоты ломают layout при крупном шрифте.

Common mistakes

  • Сводить «Auto Layout и как создавать constraints программно по сравнению с Interface Builder» к синтаксису и не объяснять reuse pool.
  • Игнорировать жизненный цикл, основной поток или момент освобождения ресурсов в сценарии uikit-4.
  • Выбирать API по привычке, не проверяя состояние, ошибки, доступность и платформенные ограничения.

What the interviewer is testing

  • Формулирует точную модель для «Auto Layout и как создавать constraints программно по сравнению с Interface Builder» и подтверждает ее корректным примером.
  • Умеет связать ответ с иерархия view controller, тестированием и отладкой на устройстве.
  • Называет ограничения подхода uikit-4, включая производительность, память и сопровождение.

Sources

Related topics