SwiftUI View Builder / 4 min

В этой статье я расскажу про то, как работает создание интерфейса в SwiftUI при помощи ViewBuilder

SwiftUI View Builder / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

В этой статье вы прочитаете:

  • Почему у ViewBuilder такой формат

  • Как создать свой контейнер для View

  • 3 способа преобразовать результат ViewBuilder в массив

Примеры изображений и кода из этой статьи доступны на GitHub

Почему у ViewBuilder такой формат

Если посмотреть на протокол View, то видно, что body - это переменная, которая возвращает объект, реализующий протокол View, который должен в итоге получить конкретный тип. И следующий код будет прекрасно работать. Следующий код будет работать

SwiftUI View Builder / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Код примера выше

Но Apple стремится упростить синтаксис для SwiftUI, поэтому у body есть два префикса - ViewBuilder и MainActor.

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

На выходе создается объект типа AnyView<TupleView<(...)>>. Т.е. следующие два блока кода эквивалентны

SwiftUI View Builder / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Код примера выше

Как создать свой контейнер для View

Для того, чтобы создать вертикальный стек с общим стилем элементов, который можно переиспользовать(с фоном, границей, тенью) можно создать свой контейнер и применить стили ко всем внутренним элементам, включая Spacer

Для этого необходимо объявить Generic структуру, в которой параметром шаблона будет Content типа View. Необходимо объявить свойство content с типом ViewBuilder, которое будет блоком-фабрикой.

А применить стиль можно с помощью обертки этого content в необходимый существующий контейнер и в Group - специальную конструкцию, которая применяет все свои модификаторы к тому, что находится внутри.

SwiftUI View Builder / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Код примера выше

Также Group можно использовать, чтобы применить какой-то стиль к View, созданному по условию

SwiftUI View Builder / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

ViewExtractor и как преобразовать содержимое ViewBuilder в массив

Увы, способ выше не позволяет каким-то образом изменить порядок дочерних элементов, добавить между ними какие-то объекты или использовать какое-то форматирование, зависящее от количества элементов.

Первый путь

Библиотека ViewExtractor. Она дает достаточно простой синтаксис

SwiftUI View Builder / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

В ней используется тайное знание о том, как устроен SwiftUI, _VariadicView.Tree хранит к себе элементы из ViewBuilder. Но это использование приватного API и Apple может заблокировать такое приложение или оно может сломаться после обновления операционной ситсемы.

Второй путь

Манипуляции c памятью в расширении над ExtensionView. Суть в том, чтобы взять из TupleView дочерние объекты и сказать, что эта область памяти - массив.

SwiftUI View Builder / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Код примера выше

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

SwiftUI View Builder / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Код примера выше

Из-за того, что ViewBuilder все оборачивает в AnyView, content[index] не сможет использоваться для сравнения типа, но index и количество объектов позволяют добавить разделители, если необходимо.

Третий путь

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

Плюсы

  • Можно сортировать и фильтровать объекты

  • Сохраняется информация о типах и можно изменять отдельные параметры

  • Доступен индекс объекта и их количество

Минусы

  • Синтаксис требует квадратных скобок и запятых

  • Каждый класс должен соответствовать протоколу через extension

  • Иногда ломается компилятор и выдает не ту ошибку, которая случилась

SwiftUI View Builder / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Код примера выше

Придется отказаться от синтаксиса ViewBuilder, но такой способ не отвалится внезапно от обновления iOS и даст возможность сортировать и фильтровать объекты, а также обращаться к их типам.

SwiftUI View Builder / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Код примера выше

Заключение

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

Все работающие примеры preview есть в проекте на Github, их можно скачать и запустить.

Пишите в комментариях интересующие вас темы для будущих статей, а чтобы их не пропустить - подписывайтесь на дневник.