AsyncImage in LazyVGrid, images are "stretched"

Viewed 99

I have a "gallery" using LazyVGrid,

struct ContentView: View {
    @State private var results = [Result]()
    
    let columns = [
        GridItem(.flexible(), spacing: 1),
        GridItem(.flexible(), spacing: 1),
        GridItem(.flexible(), spacing: 1),
    ]

    var body: some View {
        ScrollView {
            LazyVGrid(columns: columns, spacing: 1) {
                ForEach(results, id: \.self) { result in
                    
                    AsyncImage(url: URL(string: result.urls.small)) { image in
                        image
                            .resizable()
                            .aspectRatio(1, contentMode: .fit)
                    }
                }
            }
        }
}

No matter what I specify as parameter for

.aspectRatio(1, contentMode: .fit)

Both "fit" and "fill" result in the images being "stretch" i.e. not respecting their aspect ratio. What is want is them to respect their aspect ratio AND fill the entire space. See the image below of how they look right now

This is how they look right now

1 Answers

yes that's correct, need exactly square cells

In such case we need to give a container that preserve needed square shape in grid time and fill that container with image having own aspect (independent of grid).

Here is a possible solution, tested with Xcode 13.4 / iOS 15.5

demo

Main part:

ForEach(results, id: \.self) { result in
    Color.clear.overlay(
        AsyncImage(url: URL(string: result)) { phase in
            switch phase {
            case .success(let image):
                image
                    .resizable()
                    .scaledToFill()    // << for image !!
            default:
                Image(systemName: "photo")
            }
        }
    )
    .frame(maxWidth: .infinity)
    .aspectRatio(1, contentMode: .fit) // << for square !!
    .clipped()
}

Test module on GitHub

Related