skip to Main Content

I have varying amount of data that needs to be displayed as per image below, notice how when there is odd amount of data, grid adopts to stretch respected items to span across 2 columns

enter image description here

So far, my best stab at this was to have a LazyVGrid like this

LazyVGrid(
  columns: [GridItem(.adaptive(minimum: thirdOfScreen, maximum: .infinity))], 
  spacing: 8
) {
 // ...
}

here thirdOfScreen is essentially what it says, a third of the screen width. This was my attempt to force only 2 items per row (used third and not half to account for various paddings between and around items).

This worked to an extent, however if there are 1 or 3 items, they don’t stretch to full width of the row.

2

Answers


  1. You can use Grid and GridRow to define the layout, and use gridCellColumns to specify the space each cell will occupy within the grid.

    In this example, the grid is divided into 4 columns. Each GridRow specifies how many of those columns each cell will occupy.

    Following this, you can implement your logic to know if the last GridRow would have one or two cells.

    struct ContentView: View {
    
      var body: some View {
        Grid {
          GridRow {
            Color.pink
              .gridCellColumns(2) // Takes up 2 out of 4 columns
            Color.green
              .gridCellColumns(2) // Takes up the remaining 2 columns
          }
          GridRow {
            Color.orange
              .gridCellColumns(1) // Takes up 1 out of 4 columns
            Color.blue
              .gridCellColumns(1) // Takes up 1 out of 4 columns
            Color.purple
              .gridCellColumns(2) // Takes up 2 out of 4 columns
          }
          GridRow {
            Color.cyan
              .gridCellColumns(4) // Takes up all 4 columns
          }
        }
      }
    }
    
    Login or Signup to reply.
  2. It is possible to do this with a LazyVStack with HStacks inside it. You want the items in each HStack to fill the HStack equally.

    All you need to do is to split the data you want to display into chunks of 2, and use .frame(maxWidth: .infinity) on each item so they fill the HStack equally.

    Here is an example implementation that uses chunks(ofCount:) from Swift Algorithms, and Extract from View Extractor.

    import Algorithms
    import ViewExtractor
    
    struct MyGrid<Content: View>: View {
        let content: Content
        
        init(@ViewBuilder content: () -> Content) {
            self.content = content()
        }
        
        var body: some View {
            Extract(content) { views in
                let chunksOf2 = views.chunks(ofCount: 2)
                LazyVStack {
                    // use the id of the first view in the chunk of 2 as the id of the ForEach
                    ForEach(chunksOf2, id: .first?.id) { rowViews in
                        HStack {
                            ForEach(rowViews) { view in
                                view
                                    .frame(maxWidth: .infinity)
                            }
                        }
                    }
                }
            }
        }
    }
    

    You technically don’t need to add any dependencies if you don’t like dependencies. It’s not hard to write a chunks(ofCount:) yourself, and View Extractor is small enough that you can easily look at its source code and write something similar yourself.

    Usage:

    struct ContentView: View {
        var body: some View {
            MyGrid {
                ForEach((0..<10).map { String($0) }, id: .self) { i in
                    Text(i)
                        .padding()
                        .frame(maxWidth: .infinity)
                        .background(.yellow)
                }
            }
        }
    }
    

    The slight downside of this approach is that animations look a bit janky if an item is inserted in/removed from the "grid", except in the last row, where the animation looks fine.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search