Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 356
mstv2020 Avatar asked Feb 08 '21 11:02

mstv2020


People also ask

What is a lazy V grid?

A container view that arranges its child views in a grid that grows vertically, creating items only as needed.


2 Answers

  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.0) { 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.

like image 111
Beau Nouvelle Avatar answered Nov 02 '22 04:11

Beau Nouvelle


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

like image 32
Watery Desert Avatar answered Nov 02 '22 05:11

Watery Desert