AutoLayout: UIImageView and Aspect Fit content mod

2019-07-03 05:01发布

问题:

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]).

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.

回答1:

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()
}


回答2:

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 "<="