Apple представляет новый протокол макета SwiftUI с выпуском iOS 16. В этом посте я расскажу о протоколе и реализую настраиваемое представление.

Apple представляет новый протокол SwiftUI Layout с выпуском iOS 16. Это мощный инструмент для создания пользовательских представлений с элегантностью SwiftUI. В этом посте я расскажу, что такое Layout и как его можно использовать.

В конце мы создадим пользовательское табличное представление, которое автоматически упорядочивает свои подпредставления. Полный код предоставляется!

Соответствие макету

Обсуждаемый Layout — это новый протокол, который позволяет вам выбирать способ упорядочивания ваших представлений.

Через него вы буквально можете сказать, по каким координатам вы хотите разместить подпредставления. Например, теперь HStack, VStack и ZStack можно легко реализовать через него в iOS 16.

protocol Layout : Animatable

Чтобы соответствовать протоколу, вам нужно определить два метода

func sizeThatFits(
    proposal: ProposedViewSize,
    subviews: Self.Subviews,
    cache: inout Self.Cache
) -> CGSize


func placeSubviews(
    in bounds: CGRect,
    proposal: ProposedViewSize,
    subviews: Self.Subviews,
    cache: inout Self.Cache
)

Вы также можете определить makeCache(subviews:), если в вашем макете есть некоторые вычисления, которые не зависят от предложения и зависят только от подвидов. Затем вы можете произвести расчеты в makeCache(subviews:), а затем использовать эти значения.

Метод sizeThatFits

func sizeThatFits(
    proposal: ProposedViewSize,
    subviews: Self.Subviews,
    cache: inout Self.Cache
) -> CGSize

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

💥 Возможен возврат только конечных размеров. Возврат размера с бесконечной координатой приводит к сбою без разумного стека вызовов, поэтому следите за размерами, которые вы возвращаете.

Чтобы вычислить его, вы можете использовать переданные аргументы:

предложение

По сути, это предложение SwiftUI для размера вашего представления. Мне нравится думать об этом как о переговорах.

Я могу дать вам столько места. Какой у тебя будет размер? Вы вообще подойдете?

— переговорщик SwiftUI

ProposedViewSize похож на CGSize, который также может иметь определенные значения.

  • Предложение zero; представление отвечает своим минимальным размером.
  • Предложение infinity; представление отвечает своим максимальным размером.
  • Предложение unspecified; вид отвечает своим идеальным размером.

Вы также можете получить доступ к width и height предложения, если оно не соответствует указанным выше значениям.

Предложение может иметь одно фиксированное измерение, а второе — nil. Например, HStack может измерять гибкость ширины своих подпредставлений, используя при этом фиксированное значение высоты.

подвиды

Это просто контейнер прокси подвидов LayoutSubview. Через него вы можете спросить подвиды об их размере, а также дать им свое предложение

Уважаемый subview, я даю вам столько места. Какой у вас будет размер?

— Индивидуальный переговорщик макетов

Вы можете запросить размер подвида через

func sizeThatFits(ProposedViewSize) -> CGSize

и

func dimensions(in: ProposedViewSize) -> ViewDimensions

тайник

Это кеш, предоставляемый вашей функцией makeCache(subviews:). Также может быть Void (без кеша).

Метод placeSubviews

func placeSubviews(
    in bounds: CGRect,
    proposal: ProposedViewSize,
    subviews: Self.Subviews,
    cache: inout Self.Cache
)

Вот где происходит волшебство. В этом методе (и только в этом) вам даются границы для вашего представления и подпредставления для вашего распоряжения.

Чтобы разместить подпредставления, вам нужно вызвать метод place для subviews элементов.

func place(
    at position: CGPoint,
    anchor: UnitPoint = .topLeading,
    proposal: ProposedViewSize
)

Определение довольно понятное. Для каждого подпредставления вам необходимо указать точку для его размещения, якорь для этой точки и ваше предложение для выбранного подпредставления.

границы

Это границы для вашего представления. Это один из ваших sizeThatFits выходов.

💡 Хоть он и называется bounds, на самом деле это frame. Таким образом, исходная точка также указана, и вам нужно упорядочить подпредставления относительно нее

предложение

Предложение размера, из которого контейнер сгенерировал размер, который родительский элемент использовал для создания параметра bounds.

О кэшировании

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

При изменении подвидов вызывается func updateCache(inout Self.Cache, subviews: Self.Subviews). Его реализация по умолчанию — просто вызвать makeCache(subviews:).

Создание автозаполняемой таблицы

SwiftUI имеет Grid для создания табличных структур, но что, если у вас есть неизвестное количество подпредставлений? Затем нужно как-то правильно построить GridRow.

Давайте лучше использовать новую функцию протокола Layout!

Расчет размеров

Определить размер представления результатов относительно просто.

Он использует несколько вспомогательных функций:

Размещение подвидов

Наконец, нам просто нужно аккуратно расставить виды по своим местам. Просто перебираем подвиды и вычисляем их позиции x и y.

Пример

Теперь мы можем построить таблицу с нужным количеством столбцов так же просто, как обычное представление.

И он волшебным образом снова собирается после изменения количества столбцов до трех.

Заключительные заметки

Я верю, что вы видите, насколько мощным является этот инструмент. Например, Apple создает радиальный вид в своем примере с протоколом Layout.

Таким образом, только вам решать, как размещать представления внутри вашего контейнера, и, наконец, это пространство гибкости, столь необходимое для SwiftUI в iOS 16.

Вы можете скачать полный код здесь.

Want to Connect?
This post was originally published on www.alexdremov.me.