Как сделать layout для чата с новыми сообщениями, которые появляются снизу
Что проверяют
Знаете ли вы приёмы, которыми реально собирают ленту чата, и видите ли подводные камни: подгрузку истории сверху, клавиатуру, прыжки прокрутки. Следом обычно спрашивают, что будет, если пользователь читает старые сообщения, а в этот момент приходит новое.
Как отвечать
Основных подходов два:
- перевёрнутый список:
UICollectionViewилиUITableViewсtransform = CGAffineTransform(scaleX: 1, y: -1)и таким же transform у каждой ячейки. Новое сообщение вставляется в индекс 0 и оказывается внизу, короткий диалог сам прижат к низу, а история подгружается «в конец» списка без пересчёта смещения. Плата: индикатор прокрутки уезжает не туда и правится черезverticalScrollIndicatorInsets, а порядок чтения VoiceOver и контекстные меню нужно проверять отдельно; - обычный список:
UICollectionViewс compositional layout иUICollectionViewDiffableDataSource. Короткий диалог прижимают к низу черезcontentInset.top, равный разнице высоты экрана и контента. При подгрузке старых сообщений запоминают расстояние от низа контента доcontentOffsetи восстанавливают его после вставки, иначе лента прыгнет.
В обоих случаях автопрокрутку делают по условию: если пользователь был у нижнего края, листаем к новому сообщению, если читает историю, показываем кнопку «Новые сообщения». Ячейки самоизмеряемые через Auto Layout и оценочный размер. Поле ввода крепят к view.keyboardLayoutGuide (iOS 15+), для списка ставят keyboardDismissMode = .interactive.
В SwiftUI это ScrollView с LazyVStack. Начиная с iOS 17 модификатор .defaultScrollAnchor(.bottom) открывает ленту снизу, а ScrollViewReader и scrollTo(id, anchor: .bottom) листают к новому сообщению.
Где спотыкаются
Вызывают reloadData() на каждое сообщение и безусловно листают вниз. Лента мигает, а человека выдёргивает из истории.
Зовут scrollToRow сразу после вставки, пока высоты ячеек не посчитаны. Прокрутка недолетает, правильно делать это в completion у performBatchUpdates или после layoutIfNeeded().
Переворачивают список и забывают про доступность и индикатор. На ревью это всплывает как баг VoiceOver.