До iOS 17 программный скролл в SwiftUI требовал использования ScrollViewReader. Этот контейнер давал доступ к ScrollViewProxy с единственным методом scrollTo(_:anchor:). API было односторонним: можно было задать позицию, но нельзя было прочитать, где находится пользователь. Скролл к краю или к конкретному оффсету тоже был проблемой - приходилось ставить якорные вью на границах контента.
Начиная с iOS 17, SwiftUI начал менять подход. scrollPosition(id:anchor:) заменил прокси-подход на привязку к состоянию. А iOS 18 добавил еще больше возможностей.
Как задать начальную позицию:
По умолчанию ScrollView показывает верхнюю часть контента. defaultScrollAnchor(_:), появившийся в iOS 17, позволяет изменить это поведение. Типичный пример - чат, где контент должен начинаться снизу.
ScrollView {
LazyVStack {
ForEach(messages) { message in
MessageView(message: message)
}
}
}
.defaultScrollAnchor(.bottom)
В iOS 18 появилась более точная перегрузка defaultScrollAnchor(_:for:), которая принимает ScrollAnchorRole. Например, роль alignment позволяет центрировать небольшой контент, сохраняя нижнюю привязку для длинных списков.
Программный скролл через scrollPosition:
iOS 17 добавил scrollPosition(id:anchor:) для управления скроллом через привязку к состоянию. iOS 18 расширил API: появился scrollPosition(_:anchor:) с привязкой к ScrollPosition. Это структура, которая может хранить позицию как идентификатор, край или сырой оффсет.
Пример: кнопка для перехода к последнему сообщению в чате. Объявляем ScrollPosition, передаем в модификатор и вызываем scrollTo(id:) при нажатии.
struct ChatView: View {
let messages: [Message]
@State private var position = ScrollPosition()
var body: some View {
ScrollView {
LazyVStack {
ForEach(messages) { message in
MessageView(message: message)
.id(message.id)
}
}
}
.defaultScrollAnchor(.bottom)
.scrollPosition($position)
.onAppear {
position = ScrollPosition(id: messages.last?.id)
}
.safeAreaBar(edge: .bottom) {
Button {
withAnimation {
position.scrollTo(id: messages.last?.id)
}
} label: {
Text("Перейти к последнему")
}
}
}
}
🔗 Читать подробнее
💡 Вывод:
SwiftUI прошел путь от ScrollViewReader с односторонним управлением до гибкого API с двусторонней связью. Теперь можно не только двигать скролл, но и читать его состояние.
Ключевые инструменты: defaultScrollAnchor для начальной позиции, scrollPosition для управления и чтения, onScrollGeometryChange для отслеживания геометрии.
API мощные, но с нюансами. viewID - единственный способ читать позицию при ручной прокрутке. edge, point, x и y работают только для программно установленных значений. Понимание этих различий помогает строить интерфейсы, которые естественно реагируют на действия пользователя.
Подписаться на канал:
➡️ Telegram | Max