Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using VStack with AsyncImage inside LazyVStack causes images to reload on scrolling

I'm trying to display a long list of images with titles using the new AsyncImage in SwiftUI. I noticed that when I put VStack around AsyncImage and scroll through images it's reloading images every time I scroll up or down. When there's no VStack I see no reloading and images seem to stay cached.

Is there a way to make VStack not to reload images on scrolling so I can add text under each image?

Here's a working example. Try scrolling with and without VStack.

import SwiftUI

struct TestView: View {

    let url = URL(string: "https://picsum.photos/200/300")

    let columns: [GridItem] = [.init(.fixed(110)),.init(.fixed(110)),.init(.fixed(110))]

    var body: some View {
        ScrollView {
            LazyVGrid(columns: columns) {
                ForEach(0..<20) { _ in
                    // VStack here causes to images to reload on scrolling
                    VStack {
                        AsyncImage(url: url) { image in
                            image
                                .resizable()
                                .aspectRatio(contentMode: .fit)
                        } placeholder: {
                            Image(systemName: "photo")
                                .imageScale(.large)
                                .frame(width: 110, height: 110)
                        }
                    }
                }
            }
        }
    }
}
like image 554
Maklaus Avatar asked Sep 28 '21 18:09

Maklaus


People also ask

What is the use of vstack in NumPy?

numpy.vstack () function is used to stack the sequence of input arrays vertically to make a single array. tup : [sequence of ndarrays] Tuple containing arrays to be stacked. The arrays must have the same shape along all but the first axis.

What is the difference between lazyvstack and vstack?

‘Lazy’ keyword distinguishes LazyVStack from VStack. In VStack, all views are rendered and loaded in memory as soon as view is initialized and appears on screen. In LazyVStack, only views that are visible on screen will be rendered. Views are loaded into the memory and rendered as they become visible which makes view rendering much more performant.

How do you append an array in vstack?

=VSTACK (array1, [array2],...) array The arrays to append. VSTACK returns the array formed by appending each of the array arguments in a row-wise fashion. The resulting array will be the following dimensions: Rows: the combined count of all the rows from each of the array arguments.

Why does lazyvstack align to the parent capsule?

Because LazyVStack takes entire width provided by the parent, alignment is more apparent. Take a look at the example below: Notice that in case of VStack we have to make one capsule wide enough so rest capsule views can animate to alignment changes


1 Answers

I fully agree with you that this is a weird bug. I would guess that the reason it's happening has something to do with the way LazyVGrid chooses to layout views, and that using a VStack here gives it the impression there is more than one view to show. This is a poor job on Apple's part, but this is how I solved it: just put the VStacks internal to the AsyncImage. I'm not entirely sure what the original error is, but I do know that this fixes it.

struct MyTestView: View {
    let url = URL(string: "https://picsum.photos/200/300")
    let columns: [GridItem] = [.init(.fixed(110)),.init(.fixed(110)),.init(.fixed(110))]

    var body: some View {
        ScrollView {
            LazyVGrid(columns: columns) {
                ForEach(0..<20) { i in
                    AsyncImage(url: url) { image in
                        VStack {
                            image
                                .resizable()
                                .aspectRatio(contentMode: .fit)
                            
                            Text("label \(i)")
                        }
                    } placeholder: {
                        VStack {
                            Image(systemName: "photo")
                                .imageScale(.large)
                                .frame(width: 110, height: 110)
                            
                            Text("image broken")
                        }
                    }
                }
            }
        }
    }
}
like image 159
Vera Gonzalez Avatar answered Oct 23 '22 20:10

Vera Gonzalez