0

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

enter image description here

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

enter image description here

Luca
  • 171
  • 1
  • 13
  • So basically, the issue is that the image in the first one isn't actually resizing. Is this correct? –  May 01 '23 at 19:39
  • 1
    Also, have you tried using [`.scaledToFit`](https://developer.apple.com/documentation/swiftui/view/scaledtofit()) instead of `aspectRatio`? –  May 01 '23 at 19:40
  • I think your issue is the dimensions of your image. I presume they are not standardized, and are probably wider than high. In that case, a clip shape won't work. What you need is to make a cover with a hole in it. See [this answer](https://stackoverflow.com/a/59659733/7129318). – Yrb May 01 '23 at 20:08
  • @MrDeveloper no, the issue is the mask is scaled with the fit/aspect ratio so it gets cut off. – Luca May 02 '23 at 12:20
  • @Yrb I'm having trouble using that solution on my own. How would your linked solution look in my case? – Luca May 02 '23 at 15:28

2 Answers2

0

Please try this solution:

post.picture
    .resizable()
    .scaledToFit()
    .frame(
        minWidth: 0,
        maxWidth: .infinity,
        minHeight: 439,
        maxHeight: 439
    )
    .clipped()
    .clipShape(Circle())
Fuad
  • 350
  • 10
-1

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.

Luca
  • 171
  • 1
  • 13
  • 1
    That is essentially it, though for the `.frame()` I would consider using `maxWidth:, maxHeight:` – Yrb May 02 '23 at 17:23