Найти в Дзене

Панели инструментов с узором

Оглавление

Из этой статьи вы узнаете, как создать панели для кнопок и инструментов с узором и сплошным фоном через весь экран с помощью PreferenceKey

Из этой статьи вы узнаете:

  • Как создать яркие панели инструментов с градиентной заливкой
  • Как сделать автоматическое расположение кнопок
  • Как добавить поверх панели узор

Исходный код всех примеров для этой главы доступен на Git.

Яркие панели инструментов с градиентной заливкой

-2

Панели инструментов в приложении оформлены как единый градиент, проходящий от левого верхнего угла к правому нижнему. Также по краю панели идет тонкий градиент, обозначающий границу панели. Поверх панели идет узор(к нему вернемся позже).

-3

Как я рассказывал в статье про кнопки, у приложения есть два режима - детский и родительский, которые визуально отличаются по цвету. Для того, чтобы можно было управлять подложкой панели, у неё есть параметр accessLevel.

💡 Такую схему можно использовать для разделения приложения в бесплатном режиме и с Premium подпиской. Либо реализовать темную/светлую тему, если требуется больше контроля над стилем.

Стиль панелей подразумевает отступ по 8 точек с слева и справа, по 4 сверху и снизу. Минимальный размер панели 72 точки. Ширина панели всегда должна быть равна ширине экрана. Остается добавить параметр panelLocation для размещения панели сверху или снизу экрана за счет добавления Spacer(Это что-то вроде пружины, заполняющей место в VStack - контейнере, размещающем объекты в столбик).

-4

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

Хочу подробнее рассказать про GeometryReader и elements

В примере GeometryReader - это способ в SwiftUI получить размеры элемента. Здесь он вернет размеры родительского контейнера, которые равны размеру экрана.

elements - это фабрика(ViewBuilder), которая вернет объекты, которые будут размещены на панели. Также для её использования необходимо объявить компонент как Generic. Это позволит использовать панели аналогично другим SwiftUI контейнерам.

-5

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

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

-6

Правильный градиент

Чтоб добиться такого эффекта необходимо взять полноэкранные градиенты и обрезать их по размеру видимой части панели. Но напрямую получить размер панели в SwiftUI не получится.

Чтобы получить размер панели нужно сделать следующие изменения:

  1. Добавить GeometryReader в свойство .background компонентов elements панели, где в примере выше был градиет.
  2. Поскольку блок GeometryReader - это ViewBuilder - фабрика компонентов, там нельзя сохранить размер напрямую в свойство. Необходимо добавить Color.clear в качестве виртуального компонента
  3. В этом виртуальном компоненте нужно использовать метод preference, который позволяет сохранять различные значения как определенные ключи PreferenceKey.
  4. Считать полученное значение в методе onPreferenceChange и уже в его блоке, который является просто блоком кода, сохранить полученный размер панели в свойство всего компонента панели.
💡 Я сохраняю весь CGRect, но в этом нет необходимости и достаточно сохранять только CGSize.
А метод
reduce у ToolbarFrameKey пустой, потому что значение, полученное в методе preference будет передаваться по цепочке до уровня elements и не должно меняться.
Про
PreferenceKey и reduce будет отдельная статья
-7

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

После чего полученное значение используется для создания маски для градиента. Она создается как прямоугольник, заполняющий весь экран(размер передается как параметр toolbars) и в нем вырезается "окошко", которое перекрывает контент за пределами панели.

В функции GradientMask создается Shape, который создается как два вложенных прямоугольника. Модификаторы clipShape и contentShape обрезают фон визуально и логически, чтобы фон пропускал тапы

-8

Код к примеру выше

Полный код панели инструментов c добавленными градиентными границами можно посмотреть на GitHub.

Автоматическое расположение кнопок

В панель можно разместить любые компоненты, например слайдеры, галерею наклеек или кнопки. Но для кнопок придется каждый раз писать один и тот же код - в зависимости от количества кнопок добавлять Spacer.

-9

Компонент ToolbarButtons будет принимать на вход массив кнопок типа ToolbarButton из прошлой статьи и расставлять необходимые компоненты в зависимости от количества кнопок.

-10

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

  • Если кнопок нет - возвращается Spacer высотой 64 точки
  • Если кнопка одна - слева и справа от неё добавляется по Spacer
  • Если кнопок две и больше - создается массив из именованной tuple типа (index, button) и перед каждой кнопкой кроме первой добавляется Spacer

Узор поверх панели

-11

Для добавления узора я создам компонент, который позволит менять наклон и цвет узора. Это даст возможности попробовать разные параметры и посмотреть, какой приятнее глазу.

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

-12

Поэтому необходимо в компонент узора передавать размер панели инструментов toolbarFrame. И добавить маску, аналогично градиентам.

Последняя деталь - узор должен заходить под панель статуса и низ экрана. Для этого я создаю виртуальный элемент под узором, задаю ему размер панели и указываю ему ignoresSafeArea. И считываю safeAreaInsets в свойство типа EdgeInsets. Затем это свойство я сохраняю как insets и использую для корректного расчета размера узора

-13

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

Заключение

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

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

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

Исходный код всех примеров для этой главы доступен на Git.