4

I have a UITableViewCell with a vertical UIStackView that currently has .alignment = fill and distribution = fill. Inside the stack view, I have a UILabel and a UIImageView.

enter image description here

The label should be left aligned and stretch across the width of the stackview (and it does). The image view has a content mode of aspect fit and should be centered in the middle of the stackview. I thought this was working as I wanted it to until I set a background color on the UIImageView and then realized that while the image itself appears correct (scaled and centered) the view itself stretches across the full width of the stackview (just like the label does).

Is it possible to have the label stretch the width of the stack view but not the image view? Ideally, I'd like the image view to just be the size of the scaled image and not stretch all the way to the edges of the stack view.

bmt22033
  • 6,880
  • 14
  • 69
  • 98
  • You can calculate the aspect-fit and change the frame, but... why? What does it matter if the view fills the width? – DonMag Oct 04 '18 at 15:21
  • I'm adding a gesture recognizer to the UIImageVIew so I can tell when the user taps on the image. Currently, if they tap on what appears to be "outside" the image, the gesture recognizer is triggered. My UIImageView does not normally have a background color but just as a test, I changed it to gray and that's how I realized that the image view itself was being stretched to fit the width of the stack view. – bmt22033 Oct 04 '18 at 15:23

2 Answers2

11

Make these changes:

  1. Set your stack view's alignment to .center instead of .fill.
  2. Constrain the label's width to equal the stack view's width.
  3. In code, when you set the image view's image, also create an aspect-ratio constraint on the image view, like this:

    class MyCell: UITableViewCell {
        @IBOutlet private var myImageView: UIImageView!
        private var imageViewAspectConstraint: NSLayoutConstraint?
    
        func setImage(_ image: UIImage) {
            myImageView.image = image
    
            imageViewAspectConstraint?.isActive = false
            imageViewAspectConstraint = myImageView.widthAnchor.constraint(
                equalTo: myImageView.heightAnchor,
                multiplier: image.size.width / image.size.height)
            imageViewAspectConstraint!.isActive = true
        }
    }
    

Note that, since cells can be reused, you also have to remove a prior aspect-ratio constraint if there is one. The code I posted uses imageViewAspectConstraint to save the constraint and remove it when the image changes.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
3

One approach:

  • calculate appropriate aspect-fit frame for your image

When the user taps on the image view, evaluate the tap position and only take action if the tap is within the aspect-fit frame.


Another approach:

  • calculate appropriate aspect-fit frame for your image
  • embed the UIImageView horizontally centered inside a UILayoutGuide
  • add the UILayoutGuide as the stack view's arranged subview

This will keep your label stretched across the width of the stack view (the cell) and center your image view, allowing you to detect the taps.

DonMag
  • 69,424
  • 5
  • 50
  • 86