Как анимировано развернуть ячейку UITableView по нажатию для показа полного текста
Что проверяют
Знаете ли вы, как таблица пересчитывает высоты, и помните ли, что ячейки переиспользуются. Состояние «развёрнута» должно жить в модели, а не в ячейке. Следом часто спрашивают, как сделать то же на diffable data source и почему при прокрутке развёрнутые ячейки «прыгают».
Как отвечать
Рабочая схема на self-sizing ячейках:
- вёрстка:
numberOfLinesу label зависит от состояния (например, 3 или 0), констрейнты связывают его сcontentViewсверху и снизу, у таблицыrowHeight = UITableView.automaticDimensionи разумныйestimatedRowHeight. Высоту считает Auto Layout, никаких захардкоженных 200 и 44; - состояние: в контроллере
Setс идентификаторами развёрнутых элементов. Лучше id модели, чемIndexPath: индекс сдвигается при вставках. ВcellForRowAtячейка настраивается по этому множеству; - нажатие: в
didSelectRowAtпереключаете элемент в множестве, перенастраиваете видимую ячейку (cellForRow(at:)вернёт её илиnil) и вызываетеtableView.performBatchUpdates(nil). Пустое обновление заставляет таблицу заново спросить высоты и анимировать разницу. ПараbeginUpdates()иendUpdates()делает то же самое; - альтернативы:
reloadRows(at:with:)тоже работает, но пересоздаёт ячейку, и подмена заметна глазу. С diffable data source используютreconfigureItemsв снапшоте иapply(_:animatingDifferences: true).
Для плавности поворот стрелки-индикатора делают через transform в том же UIView.animate. Скачки при прокрутке обычно лечатся точнее заданным estimatedRowHeight или кэшем уже посчитанных высот.
Где спотыкаются
Хранят isExpanded в самой ячейке. После прокрутки её переиспользуют для другой строки, и развёрнутым оказывается чужой текст, а нужный сворачивается.
Вызывают cellForRow(at:) внутри heightForRowAt. Для невидимых строк он возвращает nil, принудительный каст роняет приложение, а расчёт высоты начинает зависеть от того, есть ли ячейка на экране.
Вызывают reloadData(). Анимации нет, таблица мигает, выделение и позиция прокрутки могут сбиться, так что для плавного изменения высоты он не подходит.