XCode Version 13.0 beta (13A5155e) & Targeting iOS 14 or 15
My goal is to create a chat view in SwiftUI. This requires creating a ScrollView with content of varying heights .
After extensive debugging, I've determined that if you have views within the ScrollView that do not have a fixed height, it will stutter when you scroll to the top of the view.
––––
PROJECT: Download this project and try for yourself
struct Message: Identifiable {
let id = UUID()
var text: String
}
struct ContentView: View {
@State var items: [Message] = MockData.randomMessages(count: 100)
var body: some View {
VStack {
Button("Shuffle items") {
items = MockData.randomMessages(count: 100)
}
ScrollView {
LazyVStack(spacing: 10) {
ForEach(items) { item in
Text(item.text)
.background(colors.randomElement()!)
}
}
}
}
}
}
My conclusion right now is that LazyVStack
only works with child views that have fixed height. This issue alone prevents SwiftUI from being production ready.
Has anyone else tackled this?
RESPONSE FROM APPLE (July 27, 2021):
"On your Mac target this all works but I see there are scrolling issues on iOS. This issue is definitely a bug with SwiftUI on iOS. I recommend that rather than rewrite your app you use a UIViewRepresentable for your UIScrollView (or actually UITable / UICollection View would make the most sense here). If you use a re-usable view such as a table or collection these issues will almost certainly go away. You shouldn't need to rewrite your app but you should add a UIViewRepresentable if this issue is preventing a release."
For me the jittering was caused by having an onAppear
modifier attached to the view within the ForEach
(so it runs on every item in the collection). The jittering occurred even if the hook was running in a background thread and even if it wasn't doing anything at all.
Didn't fix it 100%, but my solution was to only add the onAppear
modifier to the elements which truly needed it. In my case, I was using onAppear
for continuous scroll (pagination). By only adding onAppear
to the nth item in the list, was able to greatly reduce the jittering to a small blip when approaching the end of the list.
Here's a good article on how to apply a modifier conditionally.
Not sure this will help anyone who can't yet bump their min version to iOS 15, but hope it does!
In iOS 15 this bug appears to be fixed.
In iOS 14 I would suggest showing the first n items (enough to fill the height of the screen with buffer) in a VStack and the rest in a LazyVStack. I found in most cases this removes the jitter.
ScrollView {
VStack(spacing: 10) {
ForEach(items.prefix(10)) { item in
Text(item.text)
.background(colors.randomElement()!)
}
}
LazyVStack(spacing: 10) {
ForEach(items.dropFirst(10)) { item in
Text(item.text)
.background(colors.randomElement()!)
}
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With