How could I use a SwiftUI LazyVGrid to create a staggered grid?

3.9k Views Asked by At

I'm trying to implement a view that contains a LazyVGrid of staggered images in a grid- as seen in the below Pinterest feed:

enter image description here

I am aware of the WaterfallGird library but I was wondering if there would be a way to implement this functionality with a LazyGrid, instead of an ordinary V/HGrid.

2

There are 2 best solutions below

4
On
  1. Split your array into the number of columns you need.
  2. Inside your LazyVGrid create 2 VStacks.
  3. Drop a ForEach in both VStacks and use each of your arrays you created earlier to fill them out.
  4. That’s it

A rough example as follows...

// split array
let splitArrays = ([TileItem], [TileItem])

ScrollView {
    LazyVGrid(*setup GridItems here*) {
        VStack {
            ForEach(splitArrays.0) { tile in
                Image(tile.image)
            }
        }
        VStack {
            ForEach(splitArrays.1) { tile in
                Image(tile.image)
            }
        }
    }
}

It's likely that this isn't very performant, but it should be able to do what you're after.

1
On

Beau Nouvelle solution is working, however, it's buggy and glitchy at least of me. Instead of using LazyVGrid if we use HStack with alignment: .top It works better.

Here is the view

var body: some View {

        HStack(alignment: .top) {            
            LazyVStack(spacing: 8) {
                ForEach(splitArray[0]) {...}
            }
            
            LazyVStack(spacing: 8) {
                ForEach(splitArray[1]) {...}
            }
        }
    }

Here is the code to split the array

    private var splitArray: [[Photo]] {
        var result: [[Photo]] = []
        
        var list1: [Photo] = []
        var list2: [Photo] = []
        
        photos.forEach { photo in
            let index = photos.firstIndex {$0.id == photo.id }
            
            if let index = index {
                if index % 2 == 0  {
                    list1.append(photo)
                } else {
                    list2.append(photo)
                }
            }
        }
        result.append(list1)
        result.append(list2)
        return result
        
    }

I know this is not performance but so far only working solution I found.

Here is the full source code

enter image description here