In a chat-like app, the newest messages are shown at the bottom of the list. As you scroll up, the older messages are loaded and displayed, similar to the endless scrolling we’re accustomed to.
There are several options available to match this use case:
- The first option is to use a List and invert it 180 degrees.
- Another option is to use a ScrollView with a LazyVStack and invert them, similar to the List approach.
- Another approach would be to cheat and fall back to the well-tested UIKit solutions.
Since SwiftUI is the future, I decided against the UIKit options (unless essential). and went for the ScrollView/LazyVStack option.
The problem is that when the items are prepended to the list, the ScrollView start-position (offset) is always the first item of the prepended list.
I cannot think of a non-hacky solution to force the ScrollView to stick with its initial offset (help is appreciated).
attaching an example code of both my reversed ScrollView and main ChatView screen.
struct ReversedScrollView<Content: View>: View {
var content: Content
init(@ViewBuilder builder: () -> Content) {
self.axis = axis
self.content = builder()
}
var body: some View {
GeometryReader { proxy in
ScrollView(axis) {
VStack {
Spacer()
content
}
.frame(
minWidth: minWidth(in: proxy, for: axis),
minHeight: minHeight(in: proxy, for: axis)
)
}
}
}
}
public struct ChatView: View {
public var body: some View {
GeometryReader { geometry in
messagesView(in: geometry)
}
}
@ViewBuilder private func messagesView(in geometry: GeometryProxy) -> some View {
ReversedScrollView {
ScrollViewReader { proxy in
LazyVStack {
ForEach(messages) { message in
ChatMessageCellContainer(
message: message,
size: geometry.size,
)
.id(message.id)
}
}
}
}
}
}