Добавить объявление

Плавные expandable-ячейки в SwiftUI List

В SwiftUI раскрывающиеся списки - штука коварная. В обычном VStack или LazyVStack анимация работает как по маслу: нажал - контент плавно появился, исчез - так же плавно скрылся. Но стоит положить такую конструкцию в List, и анимация начинает дергаться. Контент просто выскакивает, а высота ячейки меняется рывками.

Почему в List анимация ломается:


Проблема в том, что List по-своему управляет переиспользованием ячеек и вычислением их высоты. Когда внутри ячейки появляется или исчезает условный блок с if, List не всегда корректно анимирует изменение размера. SwiftUI просто не понимает, как плавно перейти от одного состояния к другому.

DisclosureGroup - встроенное решение от Apple. Оно работает плавно, но не дает кастомизировать анимацию. Если вам нужен свой дизайн (своя иконка, свои тайминги), DisclosureGroup не подходит.

Как это можно обойти:


Есть способ, который заставляет List анимироваться так, как нужно. Идея в том, чтобы анимировать не появление/исчезновение контента, а его высоту. Для этого используется протокол Animatable, который анимирует одно числовое значение от 0 до 1. В зависимости от этого значения меняется высота блока, а сам контент всегда присутствует в иерархии, но его прозрачность и положение привязаны к анимируемому значению.

Основные шаги:


  • Измерить высоту заголовка и высоту раскрывающегося контента (через GeometryReader и PreferenceKey).

  • Вычислить общую высоту ячейки как высота_заголовка + высота_контента * прогресс.

  • Раскрывающийся контент разместить в оверлее, сдвигая его вниз по мере увеличения прогресса.


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

Что важно знать:


Подход рабочий, но он требует дополнительного кода и понимания того, как SwiftUI измеряет размеры вьюх. GeometryReader и PreferenceKey - не самые тривиальные инструменты, но без них не обойтись. Демонстрация работы и примеры кода доступны по ссылке под постом.

Если вам нужен простой раскрывающийся список без кастомизации - используйте DisclosureGroup. Если дизайн требует своего подхода - описанный метод дает полный контроль над анимацией.

Вывод:


Анимация раскрывающихся ячеек в SwiftUI List - не магия, а четкое понимание того, как List обрабатывает изменения высоты. DisclosureGroup решает проблему из коробки, но ограничивает в дизайне. Кастомное решение через Animatable сложнее, зато дает полную свободу. Выбор зависит от того, что важнее: скорость реализации или точное соответствие дизайну.
15.04.2026 65 303