In my chat view, each time I load new page (items are added from top), the ScrollView
jumps to top instead of maintaining scrollPosition
.
Here is my scroll view:
GeometryReader { geometryProxy in
ScrollView(showsIndicators: false) {
VStack(spacing: 0) {
if viewModel.isLoading {
LoadingFooter()
}
messagesView
.frame(minHeight: geometryProxy.size.height - loadingFooterHeight - bottomContentMargins, alignment: .bottom)
}
}
.scrollDismissesKeyboard(.interactively)
.defaultScrollAnchor(.bottom)
.scrollPosition(id: $scrolledId, anchor: .top)
.contentMargins(.bottom, bottomContentMargins, for: .scrollContent)
.onChange(of: scrolledId, scrollViewDidScroll)
}
And this is the messages view
@ViewBuilder var messagesView: some View {
LazyVStack(spacing: 0) {
ForEach(sectionedMessages) { section in
Section(header: sectionHeaderView(title: section.id)) {
ForEach(section, id: \.id) { message in
MessageView(message: message)
.padding(.horizontal, .padding16)
.padding(.bottom, .padding8)
.id(message.id)
}
}
}
.scrollTargetLayout()
}
}
Printing the scrolledId
after a page load, I can see it hasn't changed, but the ScrollView position does.
The scroll view does not jump and the position is persisted. It's the old content shifted below the new content!
In the short video you've provided:
ScrollView
is in the top position (lets sayy: 0
) and theText("Oct 27")
is iny: 0
.Text("Oct 27")
goes below (lets sayy: 500
) and nowText("Oct 14")
is iny: 0
!Oct 13
aty: 0
.So you need to move the scroll view on where the old data goes after each load. So it seems like a consistent content size to the user some how.
Here is a pseudo code for this concept:
Note that you may want to have a more accurate position of the content before and after the load. But it will have some more calculations and out of the scope of this question.