Sizing an image to the width of screen and keep the ratio?

I have many different types of images being uploaded with different sizes. I just need to display them on a detail viewcontroller page with the full width and then adjust the height accordingly to keep the ratio. So the height can be any size, just the width gets adjusted.


I've tried aspect fit but it creates white space on the top and bottom. Is there any other way to just diplay and image having it shrink down with the same ratio and not add spacing? That seems like the standard way with anyting on the web.


Thanks for any help.

Answered by QuinceyMorris in 176431022

Um, no, you're setting a constraint on the view's height vs. width:


let image = …
let constraint = NSLayoutConstraint(
     item: view, attribute: NSLayoutAttribute.Height,
     relatedBy: NSLayoutRelation.Equal, 
     toItem: view, attribute: NSLayoutAttribute.Width,
     multiplier: image.size.height / image.size.width, constant: 0.0)


or something like that.

What screens and orientations are you expecting to support?

There's not really enough information to give much advice, but it sounds like you have a vertical stack of UIImageViews, and each is set to maintain its image's aspect ratio. That's fine for the images, but you need to get the image view to resize to the size of the image. How you'd do that depends on whether you're using autolayout or not.


With autolayout, you should be able to make use of the image view's intrinsic content size, which should reflect the size of the image, provided you don't interfere by adding higher-priority constraints on the view.


Without autolayout, you're going to have to resize the image view manually to match the aspect ratio of the image.


However, it's also dependent on how you're stacking the image view: table view, collection view, stack view, etc. As I said, not enough information to go on.

Thanks for the reply, sorry I'll give some more details.


I'm using autolayout and I have 2 vertical stack views, one with the image view/image and one with some labels. The labels have contraints setting them in and the image goes full screen, basically a top header. Everything is just in a regular view controller.


For contraints I have leading and traling set on the sides and the image to top layout 0 and image to text 14. Also the top of the text to image set at14

You have 2 vertically stacked views, or you have 2 views within 1 vertical UIStackView, or you have 2 vertical UIStackViews?


Is the image view resizing vertically at all? That is, does it get bigger when forced to do so by the image it contains, or does it clip the image? I think your next step is to figure out where the extra vertical space is coming from — inside the image view, outside the image view but inside an containing view, or some kind of white space distribution at the top level.

Sorry, I have a stack view containing an image by itself and then another stack view inside with labels.


Stack

->Image

->Stack

->Label

->Label


The imageview is not resizing, just the image inside it.


I've tried it with another blank app without stack views as well and get the same thing. I think that's just how aspect fit works but it's annoying having that space when trying to align text below the image.

I would suggest you simplify the problem by replacing the above hierarchy with just the image view. Then, add constraints for the left, right, and top to the superview and see what happens. What you want is for the image view height to be automatically constrained to its intrinsic content size, which in turn depends on the scaled image. Or you migh get an error saying that the image view is missing constraints (for the height or bottom Y position).

Okay, I did what you said. The Image contraints are: Traling space to superview 0, Leading space to superview is 0, Top space to layout guide is 0.

Still has the space but I'm not quite sure what you mean by intrinsic content size?


All the other view modes stick to the top, scale to fill, aspect fill but not aspect fit.


Thanks.

Keith

I had to actually try this in a project before I realized the gap in my thinking. When you use a mode like "aspect fit", the image can be resized to fit any image view frame. That means the image view has no intrinsic size, and there's nothing you can do with autolayout that respects a particular image size (a particular image width, in your case).


I think the easiest way to solve this is to add autolayout constraints for top, left, right, and height in IB, then modify the height constraint at run time to reflect the height of the scaled image.

Thanks for trying that out and being so willing to help.


I did what you said, it seems to be working but the only thing that I'm not sure is the height. It will vary depending on different user images.


I added this in viewDidLoad() but that constant height will need to be dynamic and I'm not sure to what exactly?


imageHeight = NSLayoutConstraint(item: mainImage, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 250)

view.addConstraint(imageHeight)


Keith

When you set the image into the image view (which you do programmatically?) you can compute the aspect ratio of the image directly. Then instead of setting a height constraint as I first suggested, you could set an aspect ratio constraint using the calculated value. That way you avoid needing to know the width before layout is done.


You will likely need to call "needLayout" after changing the constraint.

Thanks again. Still kind of following you but not sure how to do a ratio contraint, this is what I found but it doesn't show the image.

NSLayoutConstraint



imageHeight = NSLayoutConstraint(item: mainImage, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 2.0/1.0, constant: 0.0)

view.addConstraint(imageHeight)

view.setNeedsLayout()

If you add an aspect ratio constraint in IB and then use the inspector to see the constraint details, you'll see that you set an "equal" constraint between the view's width (1st item) and height (2md item), with the aspect ratio as the constraint "multiplier".

Okay I see the settings in the contraint.


First item is Main Image.Width

Second item is Superview.Height <-but how to I refer to that?



imageHeight = NSLayoutConstraint(item: bigImage!.size.width, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 2.0/1.0, constant: 0.0)

view.addConstraint(imageHeight)



Thanks.


Keith

Accepted Answer

Um, no, you're setting a constraint on the view's height vs. width:


let image = …
let constraint = NSLayoutConstraint(
     item: view, attribute: NSLayoutAttribute.Height,
     relatedBy: NSLayoutRelation.Equal, 
     toItem: view, attribute: NSLayoutAttribute.Width,
     multiplier: image.size.height / image.size.width, constant: 0.0)


or something like that.

Sorry, I haven't added contraints programatically like that and still didn't know what you meant. That worked great, thanks for all the help!


Here is everything in my view controller making it work. Now I'll try and get it working with the stack view.


class ViewController: UIViewController {
  
    @IBOutlet weak var imageHeight: NSLayoutConstraint!
    @IBOutlet weak var mainImage: UIImageView!
    override func viewDidLoad() {
      
        let headerImage = UIImage(named: "myLongImage.jpg")
        mainImage.image = headerImage
      
        let image = headerImage!
      
        let constraint = NSLayoutConstraint(
            item: mainImage, attribute: NSLayoutAttribute.Height,
            relatedBy: NSLayoutRelation.Equal,
            toItem: view, attribute: NSLayoutAttribute.Width,
            multiplier: image.size.height / image.size.width, constant: 0.0)
      
        view.addConstraint(constraint)
      
        super.viewDidLoad()
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}
Sizing an image to the width of screen and keep the ratio?
 
 
Q