Reputation: 313
At the end of the day, I'm trying to scale to fill an image into a height of X (439px in my case) and then clip it using a RoundedRectangle.
post.picture
.resizable()
.aspectRatio(contentMode: .fill)
.frame(height: 439)
.clipped()
.clipShape(Circle())
This view is inside a VStack. If that is necessary to resolve this, I'll post it too, but I don't think it is.
post.picture
is an Image
property within the Post
struct. It looks like this
struct Post: Hashable, Codable {
var id: Int
var userId: Int
var location: String
var activityId: Int
var cheerCount: Int
private var pictureLocation: String
var picture: Image {
let mainPath = "/hardcoded/path/"
if let image = UIImage(contentsOfFile: "\(mainPath)/\(pictureLocation)") {
return Image(uiImage: image)
} else {
return Image(systemName: "photo")
}
}
}
The code produces this
The issue is that the circle is cut off at the edges. I want the circle mask to be scaled down but the image to retain it's aspect ratio and size.
I am not sure how to do this. I'm using a clipShape(Circle()) to make the issue more clear but I really want to clip the image with RoundedRectangle.
The code works properly when the image
height is larger than width, and cuts off the mask shape when the width larger than the height.
It's suppose to look like this
Upvotes: 1
Views: 2776
Reputation: 1512
Best avoid using UIScreen.main.bounds.size.width
for setting view dimensions due to two reasons:
main
will be deprecated in future iOS versions.In SwiftUI, parent views do not inherently dictate the size and position of child views. This becomes particularly relevant when dealing with images set to scaledToFill
. This setting causes an image to expand until one of its dimensions aligns with the corresponding dimension of its parent view. However, without explicit constraints, this can lead to the image exceeding the bounds of the parent view if the other dimension is smaller. I think this is the biggest difference from image behaviours in UIKit.
So, we'd better explicitly define the dimensions of images. This ensures that they do not unexpectedly exceed the parent view's bounds. Here, the GeometryReader
plays a role by providing access to the parent view's size, which reflects the available screen area for the image. This allows for dynamic sizing of the image based on the device's screen dimensions or the parent view's size.
Consider the following code snippet:
GeometryReader { geometry in
post.picture
.resizable()
.scaledToFill()
.frame(width: geometry.size.width, height: 439)
.clipped()
.cornerRadius(10)
}
// Apply padding or other modifiers below this line. As GeometryReader acts as a container view and may affect layout.
Upvotes: 1
Reputation: 313
Okay, I figured it out.
The frame(height: 439)
and .fill
caused the internal width
value of the frame to be extended past the screen limits in order to keep the aspect ratio when the width > height
.
This made it so the left and right edges of the frame were cut off when width > height
. This seems like silly behaviour to me since there is nothing to indicate that the frame is larger than the screen width. Visually, the image looks clipped to the correct size, and documentation wise would imply this should work properly without explicitly setting the frame size (given that the max width of the VStack should be the width of the screen).
To fix it, I needed to place an explicit width size on the frame
post.picture
.resizable()
.scaledToFill()
.frame(width: UIScreen.main.bounds.size.width, height: 439)
.clipped()
.cornerRadius(10)
.infinity
doesn't work on the width like @Fuad suggested because that would only scale the frame to it's already large width. The frame needed to be cropped essentially.
Upvotes: 0
Reputation: 448
Please try this solution:
post.picture
.resizable()
.scaledToFit()
.frame(
minWidth: 0,
maxWidth: .infinity,
minHeight: 439,
maxHeight: 439
)
.clipped()
.clipShape(Circle())
Upvotes: 0