Exploring visionOS: Resizable Images

I am working on shipping my first visionOS app to the App Store! It is a music app, and as soon as I realised that Apple Music released two new stations, namely Love and Heartbreak, I decided to make my app, Fusion, support them.

Fixed Sized Images

I started working on a detail view and was using static artwork sizes of 500 x 500 for the given station artwork:

ArtworkImage(artwork, width: 500, height: 500)
  .scaledToFit()
  .cornerRadius(24)

But then I realised that if I resized the window, the image would distort when zoomed in, and neither would increase/decrease relative to the window.

As you can see in the above screenshot, I made the window bigger, but the image remains small.

I made the window smaller in this screenshot, but the image remains large.

Relative Sized Images

I needed to find a way to resize the image with the window, so I turned to GeometryReader from SwiftUI as usual. However, I admit that GeometryReader has always been a bit of a nightmare for me to work with. My past experiences with it still haunt me, and I dreaded the thought of trying to use it again.

I remembered the new modifier in iOS 17+ containerRelativeFrame(_:alignment:) that I could use to resize the image with the window. And to my surprise, I discovered that visionOS 1.0 supports it too! I went back to watch the video I referred to on this modifier and started exploring it.

I decided to give it a try. I took the minimum of the maximum width and height possible for the artwork and then used AsyncImage to make it resizable with an aspect ratio of 1 to maintain it while resizing. I then used containerRelativeFrame to align the image horizontally, taking up 40% of the total width of the window. This made the image look great and fit perfectly in the detail view.

Here is what the code looks like:

AsyncImage(url: artwork.url(width: min(artwork.maximumWidth, artwork.maximumHeight), height: min(artwork.maximumWidth, artwork.maximumHeight))) { phase in
  switch phase {
    case .success(let image):
      image
        .resizable()
        .aspectRatio(1.0, contentMode: .fit)
        .cornerRadius(24)
        .containerRelativeFrame(.horizontal, { length, _ in
          length * 0.4
        })
    default:
      ProgressView()
  }
}

And the result? The images now get bigger with the window and smaller with the window.

I hope this helps you with your visionOS projects! Happy coding!