swiftuilayoutswiftui-layout

How to replicate UIImageView's `scaleAspectFill` for Images inside a custom Layout?


I've defined a custom layout container by having a struct conform to Layout and implementing the appropriate methods, and it works as expected.

The problem comes when trying to display Image, as they are shown squished when just using the .resizable() view modifier, not filling the container when using .scaledToFit() or extending outside of the expected bounds when using .scaledToFill().

Undesired behaviour

I understand that this is the intended behaviour of these modifiers, but I would like to replicate UIImageView's scaledAspectFill.

Desired behaviour

I know that this can usually be achieved by doing:

Image("adriana-wide")
   .resizable()
   .scaledToFill()
   .frame(width: 200, height: 200)
   .clipped()

But hardcoding the frame like that defeats the purpose of using the Layout, plus I wouldn't have direct access to the correct sizes, either.

The only way I've managed to make it work is by having a GeometryReader per image, so that the expected frame size is known and can bet set, but this is cumbersome and not really reusable.

GalleryGridLayout() {
        
        GeometryReader { geometry in
            
            Image("adriana-wide")
                .resizable()
                .scaledToFill()
                .frame(width: geometry.size.width, height: geometry.size.height)
                .clipped()
        }

       [...]
}

Is there a more elegant, and presumably efficient as well as reusable, way of achieving the desired behaviour?

Edit: Here's the code of the Layout.


Solution

  • As the expected behaviour is to fill all the available space with the image, we can just use Color (which, when used as a view, expands to fill all the space it is given) and the Image as an overlay.

    This will effectively make the Image expand to use all the available space whilst clipping it to its bounds.

    Color.clear
         .overlay {
                        
              Image("adriana-wide")
                   .resizable()
                   .scaledToFill()
          }
    

    Not quite the solution I was expecting, but does the trick.