Из этой статьи вы узнаете, как создать панели для кнопок и инструментов с узором и сплошным фоном через весь экран с помощью PreferenceKey
Из этой статьи вы узнаете:
- Как создать яркие панели инструментов с градиентной заливкой
- Как сделать автоматическое расположение кнопок
- Как добавить поверх панели узор
Исходный код всех примеров для этой главы доступен на Git.
Яркие панели инструментов с градиентной заливкой
Панели инструментов в приложении оформлены как единый градиент, проходящий от левого верхнего угла к правому нижнему. Также по краю панели идет тонкий градиент, обозначающий границу панели. Поверх панели идет узор(к нему вернемся позже).
Как я рассказывал в статье про кнопки, у приложения есть два режима - детский и родительский, которые визуально отличаются по цвету. Для того, чтобы можно было управлять подложкой панели, у неё есть параметр accessLevel.
💡 Такую схему можно использовать для разделения приложения в бесплатном режиме и с Premium подпиской. Либо реализовать темную/светлую тему, если требуется больше контроля над стилем.
Стиль панелей подразумевает отступ по 8 точек с слева и справа, по 4 сверху и снизу. Минимальный размер панели 72 точки. Ширина панели всегда должна быть равна ширине экрана. Остается добавить параметр panelLocation для размещения панели сверху или снизу экрана за счет добавления Spacer(Это что-то вроде пружины, заполняющей место в VStack - контейнере, размещающем объекты в столбик).
Хочу подробнее рассказать про GeometryReader и elements
В примере GeometryReader - это способ в SwiftUI получить размеры элемента. Здесь он вернет размеры родительского контейнера, которые равны размеру экрана.
elements - это фабрика(ViewBuilder), которая вернет объекты, которые будут размещены на панели. Также для её использования необходимо объявить компонент как Generic. Это позволит использовать панели аналогично другим SwiftUI контейнерам.
Это уже похоже на панель, но необходимо поработать над стилем. Градиент на панелях должен быть сквозным , он должен начинается на верхней панели и продолжается на нижней независимо от их размера.
Правильный градиент
Чтоб добиться такого эффекта необходимо взять полноэкранные градиенты и обрезать их по размеру видимой части панели. Но напрямую получить размер панели в SwiftUI не получится.
Чтобы получить размер панели нужно сделать следующие изменения:
- Добавить GeometryReader в свойство .background компонентов elements панели, где в примере выше был градиет.
- Поскольку блок GeometryReader - это ViewBuilder - фабрика компонентов, там нельзя сохранить размер напрямую в свойство. Необходимо добавить Color.clear в качестве виртуального компонента
- В этом виртуальном компоненте нужно использовать метод preference, который позволяет сохранять различные значения как определенные ключи PreferenceKey.
- Считать полученное значение в методе onPreferenceChange и уже в его блоке, который является просто блоком кода, сохранить полученный размер панели в свойство всего компонента панели.
💡 Я сохраняю весь CGRect, но в этом нет необходимости и достаточно сохранять только CGSize.
А метод reduce у ToolbarFrameKey пустой, потому что значение, полученное в методе preference будет передаваться по цепочке до уровня elements и не должно меняться.
Про PreferenceKey и reduce будет отдельная статья
После чего полученное значение используется для создания маски для градиента. Она создается как прямоугольник, заполняющий весь экран(размер передается как параметр toolbars) и в нем вырезается "окошко", которое перекрывает контент за пределами панели.
В функции GradientMask создается Shape, который создается как два вложенных прямоугольника. Модификаторы clipShape и contentShape обрезают фон визуально и логически, чтобы фон пропускал тапы
Полный код панели инструментов c добавленными градиентными границами можно посмотреть на GitHub.
Автоматическое расположение кнопок
В панель можно разместить любые компоненты, например слайдеры, галерею наклеек или кнопки. Но для кнопок придется каждый раз писать один и тот же код - в зависимости от количества кнопок добавлять Spacer.
Компонент ToolbarButtons будет принимать на вход массив кнопок типа ToolbarButton из прошлой статьи и расставлять необходимые компоненты в зависимости от количества кнопок.
- Если кнопок нет - возвращается Spacer высотой 64 точки
- Если кнопка одна - слева и справа от неё добавляется по Spacer
- Если кнопок две и больше - создается массив из именованной tuple типа (index, button) и перед каждой кнопкой кроме первой добавляется Spacer
Узор поверх панели
Для добавления узора я создам компонент, который позволит менять наклон и цвет узора. Это даст возможности попробовать разные параметры и посмотреть, какой приятнее глазу.
Узор будет создаваться как прямоугольник с плиточной заливкой изображением, отмеченным как Template. Это тип изображения, в котором используется только альфа-канал, а цвет можно задать через свойство foregroundColor
Этот прямоугольник повернут на угол относительно центра. Определить его размер можно по формулам
Поэтому необходимо в компонент узора передавать размер панели инструментов toolbarFrame. И добавить маску, аналогично градиентам.
Последняя деталь - узор должен заходить под панель статуса и низ экрана. Для этого я создаю виртуальный элемент под узором, задаю ему размер панели и указываю ему ignoresSafeArea. И считываю safeAreaInsets в свойство типа EdgeInsets. Затем это свойство я сохраняю как insets и использую для корректного расчета размера узора
Заключение
В этой статье я рассказал о создании панелей инструментов со сквозным фоном и дополнительным узором. На мой взгляд создавать таки панели в SwiftUI не сложнее, чем в UIKit, но требует опыта и фантазии.
Все работающие примеры панелей есть в проекте на Github, их можно скачать и запустить.
Пишите в комментариях интересующие вас темы для будущих статей, а чтобы их не пропустить - подписывайтесь на дневник.
Исходный код всех примеров для этой главы доступен на Git.