I'm experiencing this issue and not user if it's a swift ui bug or expected behaviour. Could not find something related to this online.
Given this view
struct ContentView: View {
var body: some View {
ScrollView(.vertical) {
LazyVStack {
// has has many items so you can scroll
ForEach((0...40), id: \.self) { index in
MyView() // (1)
VStack {
MyView() // (2)
}
}
}
}
}
}
Where MyView
is a simple view
struct MyView: View {
var body: some View {
Color.random // see https://stackoverflow.com/a/43365841/901249
}
}
When I scroll down (so the MyView (2)
is off screen) and then back up, MyView (2)
has a different color, while MyView (1)
remains unchanged the same.
If I add let _ = Self._printChanges()
in MyView
body
, MyView (2)
will print
MyView: @self changed
This happens no matter what kind of stack it is (HStack, ZStack, VStack) as long as it's in a LazyXStack it has this behaviour.
Why does a nested XStack inside a LazyYStack recreates it's content?
It's certainly an interesting case. I tried out a number of variations in an attempt to get a better understanding. Here are some observations:
If a
VStack
is used instead ofLazyVStack
then the colors are preserved. This is perhaps no big surprise because you would expect aVStack
to load the nested content once and then hold it in memory.If you change the implementation of
MyView
to this:...then you find that the colors are preserved. So it is not the view instances themselves which are being discarded, but the views returned by their
body
property.MyView
and print the index (as was suggested in one of the comments to the question):This shows that the views are only created once.
Then I tried steadily doubling the number of rows, trying to see if there was a point at which the views were being re-created (meaning, they were no longer being cached). I thought that this might cause the colors of view
1
to change, sooner or later. If you scroll up and down many times then what you find is that it steadily fills the gaps of the rows that were not created the first time, but it is difficult to tell if any rows are actually being re-created. My guess is that the cache is optimized, so anything that was recently shown is not likely to be discarded. I certainly wasn't able to see any changes in the rows I was monitoring.Conclusions from all of this
It is not surprising to find that the content of the
LazyVStack
is being discarded when it is out of view. What is perhaps more surprising, is that all cases of view1
preserve their color. But the experiments above have shown that in fact it is only the body of the nested views (view2
) that are being discarded, probably as a memory optimization. If the random color is captured ininit
instead of being chosen inbody
then these views do not change color either.