Компоновка: стеки и контейнеры
Чтобы расположить элементы на экране, SwiftUI использует стеки — контейнеры, выстраивающие содержимое по вертикали, горизонтали или слоями.
Суть урока: VStack ставит элементы столбиком, HStack — в строку, ZStack — слоями друг на друга. Вкладывая стеки друг в друга, вы собираете любой макет, как из конструктора.
Три базовых стека покрывают почти всю компоновку. VStack (vertical) выстраивает дочерние вью сверху вниз:
VStack(alignment: .leading, spacing: 12) {
Text("Заголовок").font(.title)
Text("Подзаголовок").foregroundStyle(.secondary)
}HStack (horizontal) ставит элементы в ряд, а ZStack накладывает их слоями по глубине — удобно для фона под содержимым:
HStack {
Image(systemName: "person.circle")
Text("Профиль")
Spacer() // расталкивает по краям
Text(">")
}
ZStack {
Color.blue // нижний слой
Text("Поверх").foregroundStyle(.white)
}Spacer — гибкая распорка: она занимает всё свободное место, отодвигая соседей. Параметры alignment и spacing управляют выравниванием и расстоянием. Настоящая мощь — во вложенности: стек внутри стека даёт сложные макеты.
VStack {
HStack {
Text("Слева"); Spacer(); Text("Справа")
}
HStack {
ForEach(1...3, id: \.self) { i in
Text("\(i)").padding().background(.gray.opacity(0.2))
}
}
}VStack HStack ZStack +--------+ +--+--+--+ +--------+ | A | | A| B| C| | задний | +--------+ +--+--+--+ |[передн]| | B | по горизонтали +--------+ +--------+ слоями по вертикали
Попробуй сам ▶ — запусти код прямо в браузере (Pyodide). Здесь нет Swift, но логика та же, что под капотом мобильного кода:
# Стеки как вложенные списки: моделируем структуру макета.
layout = {
'VStack': [
{'HStack': ['Слева', 'Spacer', 'Справа']},
{'HStack': ['1', '2', '3']},
]
}
def show(node, depth=0):
pad = ' ' * depth
if isinstance(node, dict):
for name, kids in node.items():
print(pad + name)
for k in kids:
show(k, depth + 1)
else:
print(pad + str(node))
show(layout)Как работает под капотом
Компоновка в SwiftUI — это переговоры между родителем и детьми. Родительский стек предлагает каждому дочернему вью доступное место; вью сообщает, сколько ему нужно; стек расставляет их и решает, кому отдать остаток. Spacer — это вью с минимальным «желаемым» размером, но максимальной гибкостью, поэтому он забирает всё свободное пространство. Понимание этого диалога объясняет, почему элементы иногда сжимаются или растягиваются не так, как вы ожидали.
Частые ошибки
- Забыть Spacer для выравнивания. Без него элементы центрируются, а не прижимаются к краям.
- Превышать лимит дочерних вью. Стек принимает до 10 прямых детей; больше — оборачивайте в Group или ForEach.
- Путать alignment стека и выравнивание текста. Это разные уровни настройки.
Best practices
- Стройте макеты вложением простых стеков, а не одним гигантским.
- Используйте Spacer и параметр spacing вместо магических отступов.
- Применяйте ForEach внутри стеков для повторяющихся элементов.
Итоги. VStack, HStack и ZStack — три оси компоновки: вертикаль, горизонталь и глубина. Spacer управляет свободным местом, а вложенность даёт сколь угодно сложные макеты. Это основа верстки любого экрана в SwiftUI.
Шире контекста
Система компоновки SwiftUI устроена как вежливый диалог между родителем и детьми, и понимание этого диалога избавляет от множества загадок «почему оно так встало». Родитель предлагает доступное место, ребёнок сообщает желаемый размер, родитель расставляет всех и делит остаток. Spacer — крайний случай: он почти не хочет места для себя, но жадно забирает всё свободное. Помимо трёх базовых стеков, в современном SwiftUI есть Grid для табличных макетов, LazyVStack и LazyHStack для длинных списков, которые создают элементы по мере прокрутки, экономя память. Но именно VStack, HStack и ZStack остаются рабочими лошадками, и почти любой экран — это их вложенная комбинация. Научившись мысленно раскладывать готовый дизайн на стеки (что идёт по вертикали, что по горизонтали, что слоями), вы сможете воспроизвести практически любой макет, а отладчик иерархии вью в Xcode поможет понять, как именно распределилось пространство.