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...
This is my goal...
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.
A container view that arranges its child views in a grid that grows vertically, creating items only as needed.
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.
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? 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.
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.
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.
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)
}
}
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
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)
}
}
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
}
})
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With