This piece of code is kind of stress test for a ScrollView
. While a ForEach
loop with 3000 items is rendering acceptably fast, 30000 takes long, and 300000 forever (I stopped after 3 minutes).
However, there are situations where you have a lot of content to scroll, imagine a timeline with zoomable scales (decade / year / month / day). In this case, there may be days across 50 years to display, and the user must have the chance to zoom in and out in order to change the scale.
The question for me is therefore: what strategies are possible with SwiftUI in order to optimize caching, prefetching etc., or to find out which part is to be displayed next after the user scrolled, and to prevent the model from calculating widths?
To be more precise: The model knows all data to display. The ScrollView
must be provided with a content view that has the width of all items to display. This width is my problem: how can it be determined in a way that makes sense, and without having all items to be available?
struct TestScrollView: View {
var body: some View {
ScrollView(.horizontal, showsIndicators: true, content: {
HStack(alignment: .bottom, spacing: 1) {
// 3000 is working, 30000 not well, 300000 takes forever:
ForEach(1 ..< 30000) { index in
Rectangle()
.fill(Color.yellow)
.frame(minWidth: 100, idealWidth: 200, maxWidth: 300, minHeight: 500, idealHeight: 500, maxHeight: 500, alignment: .bottom)
}
}
})
}
}
``
There was a trick old times for having horizontal UITableView
that is using now for reversal filling (bottom to top) that you can use it here:
now you have horizontal List
struct ContentView: View {
var body: some View {
List {
ForEach(1 ..< 30000) { index in
Text("\(index)")
.rotationEffect(.init(degrees: -90))
}
}
.rotationEffect(.init(degrees: 90))
.position(x: 10, y: 200) // Set to correct offset
}
}
Why are you using a Scrollview
instead of a List
? A List
would reuse the cells which is much better for performance and memory management. With the list when a cell scrolls out of sight, it gets removed from the view which helps greatly with performance.
For more information about the difference in performance please see here
var body: some View
{
List
{
ForEach(1..< 30000) {
index in
Rectangle()
.fill(Color.yellow)
.frame(minWidth: 100, idealWidth: 200, maxWidth: 300, minHeight: 500, idealHeight: 500, maxHeight: 500, alignment: .bottom)
}
}
}
Edit: Since I miss-read the original question because the scrollview is horizontal.
An option would be to wrap an UICollectionView into UIViewRepresentable see here for the dev talk and here for an example implementation this would let you display it as a horizontal view while getting the performance benefits of a list
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