Search code examples
iosswiftautolayoutstoryboarduistackview

Issues with dynamic height UIStackView using autolayout


I am trying to create a horizontal axis UIStackView with 2 views, a label and an image, that would look something like this:

enter image description here

enter image description here

UIStackView dynamic height autolayout

enter image description here

  • Blue is the label
  • Image is the image
  • The frame of the UIStackView is the green portion + the blue portion + image

What I am trying to achieve is

What I am trying to achieve:

  • The text needs to be bottom aligned to the stackview
  • The text can be 1 or more lines
  • There may be an image and in that case its dimensions would be fixed at 120 x 120
  • If the label grows beyond the height of the image, the image remains bottom aligned
  • There may or may not be an image in which case the label should take the full width of the stackview
  • The UIStackView needs to grow dynamically depending on which is taller, the label or the image

This is my set up in storyboard:

UIStackView storyboard

The constraints are as follows:

UIStackView storyboard constraints

UIStackView dynamic height autolayout constraint

The edge case I am struggling with is when I have a text that is quite long and needs to wrap, it shrinks the image and it looks like this:

enter image description here

I know one way to solve it would be to change the image constraints from lessThanEqual to Equal, however when I do this, the space allocated for the imageview doesn't get removed when the image is nil and prevents the label from taking the full width of the stackview

UIStackView dynamic height autolayout

How could I create the constraints in such a way that

  1. Makes sure the image at 128x128 when there is an image
  2. The label does not shrink the image when an image is present
  3. The label can take the full width of the stackview if the image is set to nil

Solution

  • Okay, quite a long post...

    I am actually not sure that you can achieve this ONLY with the help of constraints, but maybe you can do something like this:

    final class CustomImageView: UIImageView {
      
      override var image: UIImage? {
        didSet {
          isHidden = image == nil
        }
      }
      
    }
    

    So when you will set an image, ex. from url and will occur that image is nil, imageView will hide itself, and label will take full size of StackView. Or you can avoid creating a custom class for imageView, and just add this logic locally where you need it. Moreover, if you need image to always be 128x128 and not resize to smaller size of label(like 1,2 or 3 lines of height), you can use strong equal constraints and not lessThanOrEqual type.

    Edit:

    If changes will not apply to stack view layout, you can also use:

    customImageView.image = UIImage(systemName: "plus")
    stackView.layoutIfNeeded()
    

    or

    customImageView.image = UIImage(systemName: "plus")
    stackView.superview?.layoutIfNeeded()
    

    Sorry, forgot to mention this.