2

Is SwiftUI 2 grid system is able to render "Masonry like" layouts ?

A "Masonry" layout will look like this :

Masonry image layout

The remarkable feature of this layout is that content can span on multiple columns.

This does not seems automatically possible with LazyVGrid or LazyHGrid as they rely on GridItem which seems to describe a column (either fixed, flexible or adaptive).

If you think in term of column this design cannot be achieved.

Did i missed something or is it true we cannot make this kind of grid ?

1 Answer 1

1

I must admit that I haven't done much research for Lazy Grids yet, but as I saw some examples, I'm afraid that it will not be possible that way. But my programming mindset is: anything is possible. So let's make our own solution! Here's mine:

struct ContentView: View {
    let items = (1 ... 12).map { "Item \($0)" }
    var range: Range<Int> { 0 ..< Int((Double(items.count) / 3).rounded(.up)) }
    var body: some View {
        GeometryReader { geometry in
            ScrollView {
                LazyVStack { // still some laziness 
                    ForEach(range, id: \.self) { index in
                        HStack(spacing: 0) {
                            if index % 2 == 0 {
                                Text(items[index * 3])
                                    .frame(maxHeight: .infinity)
                                    .frame(width: geometry.size.width * 2/3)
                                    .background(Color(#colorLiteral(red: 0.3427395821, green: 0.7238617539, blue: 0.6179549098, alpha: 1)))
                                VStack(spacing: 0) {
                                    Text(items[index * 3 + 1])
                                        .frame(maxWidth: .infinity, maxHeight: .infinity)
                                        .background(Color(#colorLiteral(red: 0.1351453364, green: 0.1446713805, blue: 0.2671209574, alpha: 1)))
                                    Text(items[index * 3 + 2])
                                        .frame(maxWidth: .infinity, maxHeight: .infinity)
                                        .background(Color(#colorLiteral(red: 0.9248386621, green: 0.3957888484, blue: 0.3508865833, alpha: 1)))
                                }
                                .frame(maxHeight: .infinity)
                                .frame(width: geometry.size.width * 1/3)
                            } else {
                                VStack(spacing: 0) {
                                    Text(items[index * 3])
                                        .frame(maxWidth: .infinity, maxHeight: .infinity)
                                        .background(Color(#colorLiteral(red: 0.1351453364, green: 0.1446713805, blue: 0.2671209574, alpha: 1)))
                                    Text(items[index * 3 + 1])
                                        .frame(maxWidth: .infinity, maxHeight: .infinity)
                                        .background(Color(#colorLiteral(red: 0.9248386621, green: 0.3957888484, blue: 0.3508865833, alpha: 1)))
                                }
                                .frame(maxHeight: .infinity)
                                .frame(width: geometry.size.width * 1/3)
                                Text(items[index * 3 + 2])
                                    .frame(maxHeight: .infinity)
                                    .frame(width: geometry.size.width * 2/3)
                                    .background(Color(#colorLiteral(red: 0.226172477, green: 0.3690122366, blue: 0.3273729682, alpha: 1)))
                            }
                        }
                        .frame(height: geometry.size.width * 6/16)
                    }
                }
            }
        }
        .foregroundColor(.white)
    }
}

Explanation:

  • The line with let items = ... is just a quick way to generate a list with strings "Item 1" through "Item 12".
  • Let's consider each group of three items as a "row". Then the variable range will contain the number of rows.
  • For each row, we create an HStack. Inside it, there will be one big item and two smaller ones in a VStack. In the even rows (index % 2 == 0), the VStack will be on the right, in the odd rows, it will be on the left.
  • We use .frame(maxHeight: .infinity) to make the items fill the available space.
  • But how do we get the right item at the right place? Well, as you can see, I worked with index * 3, index * 3 + 1 and index * 3 + 2, where index is the row number. I will illustrate that with the table below:
    +-------------------------------------------+-------------------------------------------+-------------------------------------------+-------------------------------------------+
    | index = 0                                 | index = 1                                 | index = 2                                 | index = 3                                 |
    +-----------+---------------+---------------+-----------+---------------+---------------+-----------+---------------+---------------+-----------+---------------+---------------+
    | index * 3 | index * 3 + 1 | index * 3 + 2 | index * 3 | index * 3 + 1 | index * 3 + 2 | index * 3 | index * 3 + 1 | index * 3 + 2 | index * 3 | index * 3 + 1 | index * 3 + 2 |
    |    = 0    |      = 1      |      = 2      |    = 3    |      = 4      |      = 5      |    = 6    |      = 7      |      = 8      |    = 9    |      = 10     |      = 11     |
    +-----------+---------------+---------------+-----------+---------------+---------------+-----------+---------------+---------------+-----------+---------------+---------------+
    
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you @cbjeukendrup there is some tricks i am gonna use ! The thing with Masonry is that you cannot predict the layout of your first HStack (below the ForEach) in advance, it depends on the dimensions of the image. With your solution the layout of each main line if hard coded (with index % 2 = 0)
Yeah, that's true. But indeed I think you can use some of the logic! I believe I once came across another example, when I find it again, I will show it to you!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.