Avatar ·

Как программно добавить Auto Layout constraints в Swift?

Всем привет! Я новичок в iOS разработке и пытаюсь разобраться с версткой экранов программно, без использования Storyboard. Я прочитал, что для адаптивности нужно использовать Auto Layout, но никак не могу понять, как правильно добавлять constraints (ограничения) через код.

Вот простой пример: я хочу разместить синюю вью (UIView) в центре экрана, с отступами по 20 пикселей от левого и правого края, и высотой 200 точек. Я создаю вью и добавляю её на экран, но она не отображается или отображается неправильно.

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let myView = UIView()
        myView.backgroundColor = .blue
        myView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(myView)
        
        // Как тут добавить constraints?
        // Пытался так, но не работает:
        // myView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    }
}

Я знаю, что нужно отключать translatesAutoresizingMaskIntoConstraints, но дальше я запутался. Почему-то вью не появляется, хотя я добавляю constraint. Подскажите, как правильно программно добавлять ограничения? Желательно с подробным объяснением и примерами.

Заранее спасибо!

<<>>

Полное руководство по программным Auto Layout constraints в Swift

Привет! Ты на правильном пути. Главная проблема в твоём коде — ты добавил только один constraint (центрирование по X), но не указал остальные. Auto Layout требует, чтобы у вью были определены все необходимые размеры и позиции. Давай разберём пошагово.

1. Основные принципы

Когда ты работаешь с Auto Layout программно, нужно помнить три вещи:

  • translatesAutoresizingMaskIntoConstraints = false — обязательно для всех вью, которые ты верстаешь через constraints.
  • Каждая вью должна иметь достаточное количество constraints, чтобы Auto Layout мог вычислить её положение и размер. Минимум: X, Y, ширина и высота (или привязка к краям супервью).
  • Constraints активируются через свойство isActive = true или групповую активацию NSLayoutConstraint.activate([...]).

2. Решение твоей задачи — центрированная вью с отступами

Вот исправленный код для твоего примера:

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let myView = UIView()
        myView.backgroundColor = .blue
        myView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(myView)
        
        // Активируем все constraints сразу
        NSLayoutConstraint.activate([
            // Центрирование по вертикали (Y)
            myView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            // Отступ слева 20 pt
            myView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            // Отступ справа 20 pt (negative constant, так как trailing — это правая граница)
            myView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            // Фиксированная высота 200 pt
            myView.heightAnchor.constraint(equalToConstant: 200)
        ])
    }
}

Теперь вью будет:

  • По центру по вертикали (Y).
  • С отступом 20 pt от левого края.
  • С отступом 20 pt от правого края (обрати внимание на знак минус).
  • Высотой ровно 200 pt.

3. Альтернативные способы добавления constraints

Можно добавлять constraints и по одному:

myView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
myView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true
// ... и так далее

Но лучше использовать NSLayoutConstraint.activate — это более производительно и чище.

4. Частые ошибки новичков

  • Забыть отключить translatesAutoresizingMaskIntoConstraints — без этого constraints конфликтуют с автоматическими рамками.
  • Не добавить вью в иерархию — сначала addSubview, потом constraints.
  • Указать недостаточно constraints — например, только ширину, но не позицию.
  • Путать leading/trailing с left/right — для интернационализации лучше использовать leading/trailing.

5. Продвинутый пример: вложенные вью

Если нужно разместить две вью рядом:

let redView = UIView()
redView.backgroundColor = .red
redView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(redView)

let greenView = UIView()
greenView.backgroundColor = .green
greenView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(greenView)

NSLayoutConstraint.activate([
    // Красная вью: слева, по центру, ширина 100, высота 100
    redView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
    redView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
    redView.widthAnchor.constraint(equalToConstant: 100),
    redView.heightAnchor.constraint(equalToConstant: 100),
    
    // Зелёная вью: справа от красной с отступом 20, по центру, такого же размера
    greenView.leadingAnchor.constraint(equalTo: redView.trailingAnchor, constant: 20),
    greenView.centerYAnchor.constraint(equalTo: redView.centerYAnchor),
    greenView.widthAnchor.constraint(equalTo: redView.widthAnchor),
    greenView.heightAnchor.constraint(equalTo: redView.heightAnchor)
])

Надеюсь, теперь стало понятнее! Практикуйся, и через пару дней будешь верстать любые экраны без Storyboard.

<<>>

Альтернативный подход: использование NSLayoutAnchor с визуальным форматом (VFL)

Кроме Anchor API (который описан в первом ответе), существует ещё один способ — Visual Format Language (VFL). Он менее популярен, но иногда удобен для создания нескольких constraints одной строкой.

Вот как можно решить твою задачу с помощью VFL:

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let myView = UIView()
        myView.backgroundColor = .blue
        myView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(myView)
        
        // Создаём словарь с вьюхами (обязательно для VFL)
        let views = ["myView": myView]
        
        // Горизонтальные constraints: отступы 20 слева и справа
        let horizontalConstraints = NSLayoutConstraint.constraints(
            withVisualFormat: "H:|-20-[myView]-20-|",
            metrics: nil,
            views: views
        )
        
        // Вертикальные constraints: центрирование + высота 200
        let verticalConstraints = NSLayoutConstraint.constraints(
            withVisualFormat: "V:[myView(200)]",
            metrics: nil,
            views: views
        )
        
        // Активируем все constraints
        NSLayoutConstraint.activate(horizontalConstraints + verticalConstraints)
        
        // Дополнительно: центрирование по вертикали
        let centerY = myView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        centerY.isActive = true
    }
}

Плюсы VFL: компактная запись для нескольких вью, удобно для горизонтальных/вертикальных рядов.

Минусы: сложнее читается, не поддерживает все типы constraints (например, соотношения сторон), требует словарь вьюх.

Лично я рекомендую использовать Anchor API — он более современный, читаемый и безопасный. VFL стоит знать для поддержки старого кода.