4

I'm trying to place an UIImageView in the parent view's center. Image must keep its original aspect ratio and also it shouldn't exceed parent's bounds. So landscape images should be limited by parent's width and portrait images should occupy as much vertical space as needed keeping original ratio.

Sounds like quite a simple task for AutoLayout, right? Here's my setup for UIImageView:

  • center vertically in parent
  • center horizontally in parent
  • imageView.width <= superview.width
  • imageView.height <= superview.height

I also set contentMode to Aspect Fit. Everything works really great for small images which are smaller than device screen but for some reason my UIImageView takes more space than its underlying UIImage for large images (notice the green background - imageView.backgroundColor = [UIColor greenColor]).

enter image description here

Why is this happening? I am not an AutoLayout expert by my constraints look reasonable to me. If I use smaller images like 200x400 then UIImageView takes exactly 200x400 points on the screen with no extra green areas.

I'd like to add borders and rounded corners which obviously won't work properly in this case.

fraggjkee
  • 3,524
  • 2
  • 32
  • 38

2 Answers2

3

This is happenning because you've set width and height constraints as <=. For small images imageView's frame is calculated without any issues and all constraints are satisfied. But when a big image is set, imageView's size is going to be set so that the image fits, but at the same it is limited by the size of the superview (screen size).

  • If you know aspect ratio for sure, you can set it as a constraint and you won't see any green background.
  • Otherwise just leave your constraints as they are and set background color to clear color. This way any unoccupied zones will be transparent and any image you set will take maximum space in at least one dimension.

Edit:

Probably not the best solution, kind of oldschool. You can add strict width and height constraints and calculate them manually using image's aspectRatio:

@IBOutlet weak var heightConstraint: NSLayoutConstraint!
@IBOutlet weak var widthConstraint: NSLayoutConstraint!

func setImage(image: UIImage) {
    imageView.image = image
    let screenSize = UIScreen.main.bounds.size

    let imageAspectRatio = image.size.width / image.size.height
    let screenAspectRatio = screenSize.width / screenSize.height

    if imageAspectRatio > screenAspectRatio {
        widthConstraint.constant = min(image.size.width, screenSize.width)
        heightConstraint.constant = widthConstraint.constant / imageAspectRatio
    }
    else {
        heightConstraint.constant = min(image.size.height, screenSize.height)
        widthConstraint.constant = heightConstraint.constant * imageAspectRatio
    }
    view.layoutIfNeeded()
}
alexburtnik
  • 7,661
  • 4
  • 32
  • 70
  • I don't know aspect ratio beforehand unfortunately since I need to show different images in this view: big, small, landscape, portrait or even square. The image from the screenshot has 3264x2448 resolution and actually it is downscaled properly: it keeps its ratio, it isn't truncated or disstorsed.But unfortunately image view takes more physical space than the image itself. Green background was added just for test purposes. Transparent background won't work since I need to add rounded corners but these corners won't be visible since image view's edges aren't in synch with the image. – fraggjkee Oct 20 '16 at 08:43
  • But I think I can try adding a corresponding Aspect Ratio constraint programmatically though... Sounds like a plan :) But actually I can calculate and set everything programatically without AutoLayout at all so the idea of this question was to understand whether it is achievable without writing any Swift / Objective-C code. – fraggjkee Oct 20 '16 at 08:54
  • @fraggjkee: Ok, but if your plan fails, just use my function from the updated answer. I've tested it with big and small, portrait and landscape images, seems to work as expected. – alexburtnik Oct 20 '16 at 09:30
  • yeah, aspect ratio constraint was the key to solve my issue, thanks @alexburtnik. but instead of calculating width/height programatically I only calculate aspect ratio and update a corresponding constraint,. – fraggjkee Oct 20 '16 at 21:12
  • I have a similar issue where I'm putting an imageview and several other views in a scroll view. I limit the imageview to the width of the main view, but what happens is that autolayout seems to make its calculations based on the full size image, not the fit image, so it creates extra space both above and below the image, even though there are constraints in place to put the image flush against the view above and the view below it. – Victor Engel Nov 08 '17 at 23:45
1

Try to check "Clip to Bounds" for imageView in Interface Builder Clip to Bounds Image View

and you must have a well-defined height and width, not "<="

Cristina
  • 98
  • 2
  • 13
  • 1
    This is a lifesaver! I had a big issue with an imageview that has an aspect ratio constraint but also has aspect fill in content mode. the imageview just keeps extending and covering other content. Don't even know how to describe the problem but when I unintentionally reading this 0 voted answer, it literally just saved my day! Thank you Cristina! – An Chin Aug 06 '20 at 22:10