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.