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

1.7k views Asked by At

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.

3

There are 3 answers

3
vadian On BEST ANSWER

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)
1
West1 On

You can use GeometryReader, as seen here.

Example usage:

struct MyView: View {
    var body: some View {
       GeometryReader { geo in
           //You now have access to geo.size.width and geo.size.height
       }
    }
}
0
jp21 On

It feels wrong to hardcode the width and height for frame here or is that the way to go about removing the gaps between the squares?

To remove the gaps between the squares, just modify the HStack / VStack to include spacing:

HStack(alignment: .center, spacing: 0, content: {
            Rectangle()
                .fill(Color.gray)
                .frame(width: 100, height: 100)

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