I am showing the conversation in the view, initially only the end of the conversation is loaded. To simplify it's something like this:
ScrollViewReader { proxy in
ScrollView {
LazyVStack {
ForEach(items) { item in
itemView(item)
.onAppear { prependItems(item) }
}
.onAppear {
if let id = items.last?.id {
proxy.scrollTo(id, anchor: .bottom)
}
}
}
}
}
func prependItems(item: Item) {
// return if already loading
// or if the item that fired onAppear
// is not close to the beginning of the list
// ...
let moreItems = loadPreviousItems(items)
items.insert(contentsOf: moreItems, at: 0)
}
The problem is that when the items are prepended to the list, the list view position remains the same relative to the new start of the list, and trying to programmatically scroll back to the item that fired loading previous items does not work if scrollbar is moving at the time...
A possible solution I can think of would be to flip the whole list view upside down, reverse the list (so that the new items are appended rather than prepended), then flip each item upside down, but firstly it is some terrible hack, and, more importantly, the scrollbar would be on the left...
Is there a better solution for backwards infinite scroll in SwiftUI?
EDIT: it is possible to avoid left scrollbar by using scaleEffect(CGSize(width: 1, height: -1))
instead of rotationEffect(.degrees(180))
, but in either case item contextMenu is broken one way or another, so it is not a viable option, unfortunately, as otherwise scaleEffect
works reasonably well...
EDIT2: The answer that helps fixing broken context menu, e.g. with a custom context menu in UIKit or in some other way, can also be acceptable, and I posted it to freelancer in case somebody is interested to help with that: https://www.freelancer.com/projects/swift/Custom-UIKit-context-menu-SwiftUI/details