13

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)
                        }
                    }
                }
            }
        }
    }
}
Maklaus
  • 538
  • 2
  • 16
  • 37
  • 1
    What a bug with pretty basic layout! I think this makes `AsyncImage` not usable, it's strange that it wasn't reported earlier and fixed before release. Report it to [Feedback Assistant](https://feedbackassistant.apple.com/) and consider switching to this [AsyncImage](https://github.com/V8tr/AsyncImage): it's pretty stable – Phil Dukhov Oct 01 '21 at 08:37
  • Unfortunately, AsyncImage does not cache the images. so each time it downloading an image on the reused cell. – Raja Kishan Oct 02 '21 at 11:29
  • I am using SDWebImage, still I have this issue. Anyone knows how to solve this? – Akhil Oct 24 '22 at 09:17

1 Answers1

11

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")
                        }
                    }
                }
            }
        }
    }
}
Vera Gonzalez
  • 474
  • 2
  • 5