Взглянем на новый механизм компоновки

Если бы мне пришлось назвать аспект разработки SwiftUI, который меня сначала сбил с толку, то это были бы руководства по выравниванию. Поэтому я решил написать эту статью, чтобы попытаться немного демистифицировать их, наметить, как вы можете с ними работать, и действительно заставить их работать на вас.

Руководства по выравниванию в SwiftUI похожи на ограничения в UIKit. Нет, я беру это обратно. Они являются ограничениями в SwiftUI - только вместо того, чтобы управлять вашим экраном на микроуровне, они управляют им на макроуровне. В наши дни вам необходимо управление макросами для экранов, потому что они бывают всех форм и размеров. Однако, как и ограничения, направляющие не всегда так просты в использовании.

Первое, что вам нужно оценить, - это семантика механизма компоновки в SwiftUI. Лучшее место, чтобы узнать об этом, - Выступление Дэйва Абрахамса и Джона Харпера на WWDC 2019 по этой теме. В нем они описывают три золотых правила верстки:

  • Родитель предлагает размер ребенку.
  • Ребенок использует предложение, чтобы определить свой размер.
  • Родитель использует размер, выбранный ребенком, для размещения указанного ребенка.

«При чем здесь направляющие для выравнивания?» вы можете спросить. Абсолютно все. Для размещения / управления пространством в SwiftUI вы используете контейнеры (ZStack, VStack и HStack). Контейнеры, которые являются родительскими в семантике выше, контейнеры, которые будут организовывать своих братьев и сестер, используя указанные правила. Вы можете повлиять на эти правила с помощью ваших руководств по выравниванию. Но подождите, есть загвоздка. Обычно семантика макета гарантирует, что направляющие макета просто не работают (по крайней мере, для фреймов). Безумно, я знаю, но именно так работает семантика по умолчанию.

Давайте рассмотрим пример. У меня есть вид, в котором я добавляю фигуру (круг). Вот код:

Он слишком большой, не правда ли? Это та семантика макета. Понимаете, вы просили круг, не указывали ему размера, а он занимал столько места, сколько мог. Нет, нам нужны некоторые правила. Добавьте ZStack и фрейм.

Лучше, но ты не хотел, чтобы он был в центре. Вы хотели, чтобы это было вверху, поэтому вы проверяете документацию, добавляете направляющую для выравнивания к фрейму и пытаетесь снова. Ничего не произошло.

Вернитесь к картинке и посмотрите на красную рамку. Точка уже находится наверху. Собственно, он тоже внизу, а также на передней и задней кромках. Конечно, вы можете придать ему большую рамку, но тогда ваш круг снова займет все это. Это уловка-22.

Что вам нужно, так это две рамки. Добавьте второй ZStack и фрейм в микс с направляющей на второй фрейм.

Это очень простой принцип, который вы можете применить, чтобы заставить работать любой макет по вашему выбору. Вам просто нужно начать думать о фреймах. Apple определила девять наиболее распространенных макетов, которые могут вам понадобиться. Измените выравнивание, которое вы использовали для переменной состояния, и посмотрите на свои варианты. Вот анимированный GIF, который поможет вам понять суть дела. Эта танцующая точка обводит все варианты выравнивания кадра, когда мы меняем размер кадра. Посмотрите внимательно, и вы увидите закономерность.

Это немного похоже на ту знаменитую игру о жизни, когда я наблюдаю, как она видоизменяется. Это только начало, но определение рамок для каждого объекта в вашем проекте и руководство по выравниванию… нет, нам нужно больше. Вот код этого танцующего бота:

Пользовательские выравнивания

Пользовательские выравнивания позволяют размещать виды относительно друг друга. Чтобы определить их, вам сначала нужно определить некоторые идентификаторы выравнивания. Это код шаблона:

Код для вертикального выравнивания выглядит следующим образом:

Эта часть, наконец, связывает два уже показанных:

extension Alignment {
  static let theAlignment = Alignment(horizontal:   .theHorizontalAlignment, vertical: .theVerticalAlignment)
}

Хорошо, теперь мы будем использовать новые идентификаторы выравнивания вместе с модификатором вида alignmentGuide, чтобы выровнять два вида в кадре относительно друг друга. Конечный результат выглядит так - парочка танцующих ботов:

Что мы здесь смотрим? Ну, пользовательское выравнивание предусматривает, что вам нужно начать с центра, а затем добавить свои виды относительно него и друг друга. Таким образом, оба круга будут оставаться в центре, но произвольно танцевать вокруг передних / задних, верхних / нижних краев друг друга. Как и в другом примере, мы меняем размер рамки, в которой они находятся, чтобы помочь вам понять, что происходит. Обратите внимание, что идентификатор выравнивания здесь связан со стеком viewModifier, а не с фреймом, как в первом случае.

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

Больше настраиваемых представлений

Мы еще не закончили. Как видите, существует вторая версия модификатора представления alignmentGroup, которая основана не на нижнем индексе, как показано выше, а на вычисленном значении:

.alignmentGuide(.myHorizontalAlignment, computeValue: {d in viewW})
.alignmentGuide(.theVerticalAlignment, computeValue: {d in viewH})

Таким образом, значение viewW, которое вы видите в строке выше, является значением относительно ширины представления, которое он изменяет, а значение viewH - значением относительно высоты изменяемого представления. Вот анимированный GIF, чтобы попытаться воплотить это утверждение в жизнь. В нем мы меняем выравнивание viewW и viewH от -64 до 96. В нашем случае значения, кратные размеру круга, равны 32.

Как видите, круги на самом деле беспорядочно - и безумно - движутся по всей площади. Здесь нет места размещения в начале, в конце, сверху или снизу. Это все координаты. Обратите внимание, что модификатор выравнивания снова находится в модификаторах стека и представления выравнивания.

На этом я подошел к концу статьи. Надеюсь, мне удалось хоть немного демистифицировать выравнивания в процессе. Продолжайте кодировать и сохраняйте спокойствие.