Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you clip an image to a square inside a lazy grid? (SwiftUI)

Tags:

ios

swiftui

This is not a duplicate of this question.

Below you'll see a grid of rectangles with 3 columns. I'm trying to achieve the same effect but with images. The images should be clipped, but otherwise not appear stretched or distorted.

Here's how I achieved the rectangle grid...

// In my view struct...

private let threeColumnGrid = [
    GridItem(.flexible(minimum: 40)),
    GridItem(.flexible(minimum: 40)),
    GridItem(.flexible(minimum: 40)),
]

// In my body...

LazyVGrid(columns: threeColumnGrid, alignment: .center) {
    ForEach(model.imageNames, id: \.self) { imageName in
        Rectangle()
            .foregroundColor(.red)
            .aspectRatio(1, contentMode: .fit)
    }
}

This is the layout I want using Rectangles...

Rectangle Grid Image

This is my goal...

Photos App Screenshot

Update:

If I do this...

Image(item)
    .resizable()
    .aspectRatio(1, contentMode: .fit)

The images distort if their aspect ratio wasn't already 1:1. For example, the circle image in the screenshot below should be a perfect circle.

Failed Example 1

like image 439
DanubePM Avatar asked Jul 08 '20 09:07

DanubePM


People also ask

What is LazyVGrid?

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

What is grid item SwiftUI?

GridItem. In a grid layout in SwiftUI, each element is called GridItem . We can customize it by changing its size, spacing between, and aligning it to the parent view. There are three types of GridItem : Fixed — The element is fixed in size no matter the orientation or screen size of the device.

How to make a circular image in SwiftUI?

Let’s make the image circular now by applying the clipShape modifier as shown right next: The parameter value must be an instance of a type that conforms to the Shape protocol. The Circle object we provided above is a predefined shape in SwiftUI, and it takes no arguments upon initialisation.

How to create square fit photos?

How to create Square fit Photos? Select or drag photos you want to optimise for Instagram into square fit size Select background color which will be filled. "Image" means same image, but smaller opacity Max 10 images per batch.

What is a shape mask in SwiftUI?

Applying a shape mask to the displayed content of views in SwiftUI is a task performed often, and it’s most common on images. For example, an app might want to display avatar images as circles, album covers as rounded rectangles, and so on.

How to position the image inside the Clipper widget?

You can wrap the image widget with an Align widget to easily position the image inside the clipper. The widthFactor and heightFactor properties are used to decide the size of the clipper and alignment is used to decide the position of the clipper. Here we are clipping the image at the center.


4 Answers

I found a solution.

Defining the number of columns...

// Somewhere in my view struct
private let threeColumnGrid = [
    GridItem(.flexible(minimum: 40)),
    GridItem(.flexible(minimum: 40)),
    GridItem(.flexible(minimum: 40)),
]

Creating the grid...

LazyVGrid(columns: threeColumnGrid, alignment: .center) {
    ForEach(model.imageNames, id: \.self) { item in
        GeometryReader { gr in
            Image(item)
                .resizable()
                .scaledToFill()
                .frame(height: gr.size.width)
        }
        .clipped()
        .aspectRatio(1, contentMode: .fit)
    }
}
like image 144
DanubePM Avatar answered Nov 15 '22 11:11

DanubePM


I found the DPMitu's answer was not centering the image (it was aligned to the leading edge).

This worked better for me (which also doesn't require GeometryReader):

  LazyVGrid(columns: columns, spacing: 30) {
       ForEach(items) { item in
           Image("IMG_0044")
               .resizable()
               .scaledToFill()
               .frame(minWidth: 0, maxWidth: .infinity)
               .aspectRatio(1, contentMode: .fill)
               .clipped() //Alternatively you can use cornerRadius for the same effect
               //.cornerRadius(10)
       }
  }
    

I found it here https://www.appcoda.com/learnswiftui/swiftui-gridlayout.html about half way down the page

like image 37
chopsalot Avatar answered Nov 15 '22 12:11

chopsalot


Some were asking about the image appearing off center. You can adjust that by adding in the line .position(x: gr.frame(in: .local).midX, y: gr.frame(in: .local).midY). So the full thing would be:

Number of Columns

// Somewhere in my view struct
private let threeColumnGrid = [
    GridItem(.flexible(minimum: 40)),
    GridItem(.flexible(minimum: 40)),
    GridItem(.flexible(minimum: 40)),
]

Creating the Grid

LazyVGrid(columns: threeColumnGrid, alignment: .center) {
    ForEach(model.imageNames, id: \.self) { item in
        GeometryReader { gr in
            Image(item)
                .resizable()
                .scaledToFill()
                .frame(height: gr.size.width)
                .position(x: gr.frame(in: .local).midX, y: gr.frame(in: .local).midY)
        }
        .clipped()
        .aspectRatio(1, contentMode: .fit)
    }
}
like image 25
JoshHolme Avatar answered Nov 15 '22 12:11

JoshHolme


Solution without GeometryReader and images that scale correctly.

struct ContentView: View {

    private let columns = [
        GridItem(.flexible(minimum: 40)),
        GridItem(.flexible(minimum: 40)),
        GridItem(.flexible(minimum: 40)),
    ]

    private let imageNames = ["image0", "image1", "image2", "image3", "image4"];

    var body: some View {
        LazyVGrid(columns: columns, content: {
            ForEach(imageNames, id: \.self) { name in
                Color.clear
                    .background(Image(name)
                                    .resizable()
                                    .scaledToFill()
                    )
                    .aspectRatio(1, contentMode: .fill)
                    .clipped()
                    .contentShape(Rectangle()) // <-- this is important to fix gesture recognition
            }
        })
    }
}
like image 31
Murlakatam Avatar answered Nov 15 '22 13:11

Murlakatam