4

I have some issues with auto layout when building view programmatically.

There is a vertical UIStackView with several elements inside, mainly labels and images. After setting properties on stackView and imageView, as a result I get something similar to image 1 below (white spaces on top and bottom of the image; the smaller screen size the bigger white spaces) while I would like to get something more similar to image 2 (without white spaces around image).

1:

White spaces around image

2:

No white spaces around image

I was reading some tutorials on how to properly set the stackView, its distribution and alignment and as a result in my StackView properties are set like this:

myStackView.axis = .vertical
myStackView.distribution = .fill
myStackView.alignment = .fill
myStackView.spacing = 12
myStackView.contentMode = .scaleAspectFit

and on ImageView before I add it as an arranged subview, I set:

myImageView.contentMode = .scaleAspectFit
myImageView.autoresizesSubviews = true
myImageView.sd_setImage(with: URL(string: image))           
myImageView.setContentHuggingPriority(251, for: .horizontal)
myImageView.setContentHuggingPriority(500, for: .vertical)
myImageView.setContentCompressionResistancePriority(750, for: .horizontal)
myImageView.setContentCompressionResistancePriority(750, for: .vertical)

I did not change CHP and CCRP on labels above and under the imageView.

I was trying to manipulate with content hugging priority and content compressions resistance priority but it did not change anything. Any ideas what I do wrong?

Bastek
  • 899
  • 1
  • 12
  • 27
  • what is CHP and CCRP? –  Feb 06 '17 at 11:44
  • @NileshPol Content Hugging Priority and Content Compression Resistance Priority. – Bastek Feb 06 '17 at 11:45
  • probably, those things won't work inside stackview –  Feb 06 '17 at 11:46
  • @NileshPol those work inside stackView - check on [Apple Developer:](http://apple.co/2jTMRGK) – Bastek Feb 06 '17 at 11:50
  • yes, they do. That's why I used "probably". My bad –  Feb 06 '17 at 11:52
  • The problem can be solved by adding auto layout constraint for image height after image loading completed. UIImage has size property which can be used to calculate height for UIImageView. This code will fit image with saving proportions. let height = (stackView.frame.size.width / image!.size.width) * image!.size.height stackView.addConstraint(NSLayoutConstraint(item: imageView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: height)) – look1n Feb 06 '17 at 14:17
  • @AntonLookin sure I can add constraint for image height, but if I do that and then rotate device, the image is presented incorrectly inside stackView, it does not resize because of the height constraint. I would like it to update its size dynamically dependently on stackView, shouldn't it work that way? – Bastek Feb 06 '17 at 15:04
  • Update height constraint after image loaded and inside viewDidLayoutSubviews method, this will fix both issues – look1n Feb 06 '17 at 15:13

1 Answers1

17

I suspect the problem here as is that the hugging and compression-resistance priorities apply to the view's intrinsic content size, and the intrinsic content size of a UIImageView is the natural size of the image. But you want the image to be scaled to fit the width of the screen, which means that (in general) you don't want the image view's size to be the natural size of its image. Thus the hugging and compression-resistance priorities will not help you.

What you need to do is add two constraints:

  • Constrain the image view's width to equal the stack view's width. You can do this in the storyboard or in code. In code:

    myImageView.widthAnchor.constraint(equalTo: myStackView.widthAnchor).isActive = true
    
  • Constrain the image view's aspect ratio to match the image's aspect ratio. If you change the image at runtime, you need to do this in code, like this:

    // Instance variable:
    var imageAspectRatioConstraint: NSLayoutConstraint?
    
    // When you set the image:
    imageAspectRatioConstraint?.isActive = false
    imageAspectRatioConstraint = myImageView.widthAnchor.constraint(
        equalTo: myImageView.heightAnchor,
        multiplier: myImageView.image!.size.width / myImageView.image!.size.height)
    imageAspectRatioConstraint!.isActive = true
    

These two required constraints will force the image view to be exactly the right height to perfectly contain the scaled image.

You may still run into trouble if you have constrained the height of the stack view to match the height of some other view—for example, your top-level view. (If your stack view is your top-level view, it is effectively constrained to fill the screen.) In that case, the stack view will stretch or shrink at least one of its arranged subviews as needed to fill that height.

If none of the arranged subviews has a flexible height, then auto layout may end up breaking the image view's aspect ratio constraint so it can stretch the image view to fill the needed space. Or it may stretch some other view that you don't want stretched.

If that's happening, you have three options:

  • Remove the constraint that's forcing the stack view to fill the height of some other view. Then auto layout will set the height of the stack view to exactly contain its arranged subviews at their preferred sizes (based on constraints or intrinsic content size).

  • Lower the vertical hugging and/or compression resistance priority of one or more arranged subviews that you want resized to fill the required height.

  • Add a “padding” UIView to the stack view to absorb the additional height. Set its background color to nil or .clear so it's not visible.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • 2
    Would this approach work for a dynamic height UITableViewCell with a UIStackView and an image that is loaded via web? i.e the cells height will change after loading the image? - I'm having trouble with this approach but thought I should ask for insights! – royherma Jun 11 '18 at 16:14
  • Unlikely to work because the table view needs to know that the cell height needs to change. – rob mayoff Jun 11 '18 at 16:33
  • Any idea what to do in this case? Use case is: building a feed with titles and images of unknown height if that makes sense. – royherma Jun 11 '18 at 16:39