Search code examples
swiftswiftuiswiftui-layout

How to have 2x2 made of squares fit screen in SwiftUI


I'm trying to create a 2x2 grid through either VStacks/HStacks or LazyVGrid to have the squares on each row fit the screen. For example, the first and second squares each take up half the width of the screen and based on that length, that'll determine the height to make it a square. How would I go about doing that in the two ways that I've mentioned or is there a better way to do it? Here's what I have so far.

VStack {
    HStack {
        Rectangle()
            .fill(Color.gray)
            .frame(width: 100, height: 100)

        Rectangle()
            .fill(Color.blue)
            .frame(width: 100, height: 100)
    }
    HStack {
        Rectangle()
            .fill(Color.red)
            .frame(width: 100, height: 100)

        Rectangle()
            .fill(Color.green)
            .frame(width: 100, height: 100)
    }
}

Stacks

It feels wrong to hardcode the width and height for the frame property here or is that the way to go about removing the gaps between the squares? Would this way of hardcoding values scale to other phone sizes?

LazyVGrid(columns: layout) {
    Rectangle()
        .fill(Color.gray)
        .frame(width: 210, height: 210)
    Rectangle()
        .fill(Color.blue)
        .frame(width: 210, height: 210)
    Rectangle()
        .fill(Color.red)
        .frame(width: 210, height: 210)
    Rectangle()
        .fill(Color.green)
        .frame(width: 210, height: 210)
}

LazyVGrid

EDIT: Here's what the new code looks like to get what I wanted.

VStack(spacing: 0) {
    HStack(spacing: 0) {
        Rectangle()
            .fill(Color.gray)

        Rectangle()
            .fill(Color.blue)

    }
    HStack(spacing: 0) {
        Rectangle()
            .fill(Color.red)

        Rectangle()
            .fill(Color.green)

    }
}
.aspectRatio(1.0, contentMode: .fit)

enter image description here

Now to get it working with LazyVGrid.


Solution

  • Just apply the aspectRatio (value 1 and fit the screen) modifier, there is no need to specify any frame.

    VStack(spacing: 0) {
        HStack(spacing: 0) {
            Rectangle()
                .fill(Color.gray)
                
            Rectangle()
                .fill(Color.blue)
                
        }
        HStack(spacing: 0) {
            Rectangle()
                .fill(Color.red)
            
            Rectangle()
                .fill(Color.green)
                
        }
    }
    .aspectRatio(1.0, contentMode: .fit)