20

I have a UIImageView with a fixed width and height. I don't want to change the frame of the UIImageView. I want to have it hold an image where I keep the aspect ratio and I fit the width and let the image be either too tall, or too short for the UIImageView's frame. Like this:

UIImageView frame

The red is the UIImageView's frame. The gray is the actual image as it's displayed.

SirRupertIII
  • 12,324
  • 20
  • 72
  • 121

4 Answers4

33

I think the best way to do it is to play with the mode of your imageView (Aspect Fill, Aspect Width, etc) and this is based on the ratio between the width and height of the image

if image.width > image.height {
    imageView.contentMode = UIViewContentModeScaleAspectFit
    //since the width > height we may fit it and we'll have bands on top/bottom
} else {
  imageView.contentMode = UIViewContentModeScaleAspectFill
  //width < height we fill it until width is taken up and clipped on top/bottom
}

UIViewContentModeScaleAspectFit

Scales the content to fit the size of the view by maintaining the aspect ratio. Any remaining area of the view’s bounds is transparent.

UIViewContentModeScaleAspectFill

Scales the content to fill the size of the view. Some portion of the content may be clipped to fill the view’s bounds.

I haven't tested it but off the top of my head this seems right

Andy
  • 723
  • 9
  • 24
Glenn
  • 2,808
  • 2
  • 24
  • 30
  • Awesome, I'll mess around with this. Good idea. – SirRupertIII Sep 23 '15 at 22:58
  • I'm working with MPMediaItemArtwork, how to do this? – Jared Chu Nov 02 '16 at 07:22
  • 1
    this solution does not take into account the shape of the view, only the shape of the image. this solution only works if the view is square. for a wide image, if the view is wide and skinny, it will aspect fit the image when it should aspect fill. – Andy May 21 '19 at 11:11
4

I think you need to compare the image aspect ratio to the aspect ratio of the UIImageView itself:

private func updateUI() {
    guard let image = image else { return }
    let viewAspectRatio = self.bounds.width / self.bounds.height
    let imageAspectRatio = image.size.width / image.size.height
    if viewAspectRatio > imageAspectRatio {
        self.contentMode = .scaleAspectFill
    } else {
        self.contentMode = .scaleAspectFit
    }
}

override var image: UIImage? { didSet { updateUI() }}

override func layoutSubviews() {
    super.layoutSubviews()
    updateUI()
}

Note: this is aspect fit width

Andy
  • 4,441
  • 1
  • 19
  • 16
  • Could this be used to set the aspect ratio of the image to 16:9? If so, the best way to approach doing that? – Luke Irvin May 08 '20 at 17:35
  • The question was about keeping the original aspect ratio of the image, but having the image show in a view with possibly a different aspect ratio than the image. If you want to force the aspect ratio of an image, you can use constraints. In interface builder, for example, you can set the aspect ratio of your UIImageView. Is this what you are asking? – Andy May 10 '20 at 07:45
3

Swift 5.1 iOS 13

Because mine was on the header cell on a collection view this is what worked for me:

if headerCell!.imageView.frame.width > headerCell!.imageView.frame.height {
    headerCell!.imageView.contentMode = .scaleAspectFit
    //since the width > height we may fit it and we'll have bands on top/bottom
} else {
     headerCell!.imageView.contentMode = .scaleAspectFill
     //width < height we fill it until width is taken up and clipped on top/bottom
}
Luckabo
  • 74
  • 5
Marlhex
  • 1,870
  • 19
  • 27
1

For my case solution was to set UIImageView's contentMode based on if ratio of image's height and width is bigger than of imageView's.

func setupImageViewContentMode() {
    if let image = imageView.image, image.size.height / image.size.width > imageView.frame.height / imageView.frame.width {
        imageView.contentMode = .scaleAspectFit
    } else {
        imageView.contentMode = .scaleAspectFill
    }
}

Also, note that you have to setup this according to current layout, so calling this method e.g. in layoutSubviews(), in viewDidLayoutSubviews(), after image is loaded from backend or wherever you need it does the job.

Robert Dresler
  • 10,580
  • 2
  • 22
  • 40