I’m engaged on a chatbot app and need to implement a chat interface just like ChatGPT’s UI. When a person sends a message:
- The earlier messages ought to transfer out of view on the high.
- The person’s newest message ought to seem on the high.
- The assistant’s response ought to seem beneath it.
Moreover, when the person scrolls down, earlier messages ought to load naturally—with none animation glitches or abrupt jumps.
Right here is my present code:
ScrollViewReader { proxy in
ScrollView {
VStack(alignment: .main, spacing: 10) {
if streamingChatViewModel.isShowAllMessages {
// Full Message Record (Seems when isShowAllMessages is true)
ForEach(streamingChatViewModel.messages) { message in
MessageView(message: message)
.id(message.id)
}
} else {
// Present Solely Final Two Messages (Seems when isShowAllMessages is fake)
ForEach(streamingChatViewModel.messages.suffix(2)) { message in
MessageView(message: message)
.id(message.id)
}
}
}
.padding()
.background(GeometryReader { geometry in
Colour.clear
.choice(key: ScrollOffsetPreferenceKey.self, worth: geometry.body(in: .named("scroll")).origin)
})
.onPreferenceChange(ScrollOffsetPreferenceKey.self) { worth in
self.scrollPosition = worth
}
.background(GeometryReader { geometry in
Colour.clear
.onAppear {
// Measure the content material peak
updateContentHeight(geometry)
}
.onChange(of: streamingChatViewModel.messages.final?.textual content) { _ in
// Replace content material peak when messages change
updateContentHeight(geometry)
proxy.scrollTo(streamingChatViewModel.messages.final?.id, anchor: .backside)
}
.onChange(of: streamingChatViewModel.isShowAllMessages) { _ in
}
.choice(key: ContentHeightKey.self, worth: geometry.measurement.peak)
})
}.onAppear(){
withAnimation(.none){
proxy.scrollTo(streamingChatViewModel.messages.final?.id, anchor: .backside)
}
}
.body(peak: scrollViewHeight) // Set dynamic peak
.clipped() // Clip content material exterior the body
.coordinateSpace(title: "scroll") // Add a coordinate area for monitoring scroll offset
.onPreferenceChange(ContentHeightKey.self) { newHeight in
// Replace contentHeight and regulate ScrollView peak
contentHeight = newHeight
scrollViewHeight = min(newHeight, UIScreen.essential.bounds.peak * 0.7)
}
.onPreferenceChange(ScrollOffsetPreferenceKey.self) { worth in
// Monitor scroll place
self.scrollPosition = worth
adjustScrollViewHeight()
}
.body(peak: scrollViewHeight) // Set dynamic peak
.clipped()
.onChange(of: streamingChatViewModel.messages.rely) { _ in
// Scroll to the underside when a brand new message is added
proxy.scrollTo(streamingChatViewModel.messages.final?.id, anchor: .backside)
}
.onChange(of: keyboardManager.keyboardHeight) { _ in
}
}