4

I have a UIStackView with three labels whose height is determined using Dynamic Type and text that have can wildly varying lengths. The container for the stack view has a fixed width and height depending on device screen size (small on iPhone SE, for example.) I want to center the stack view within the container (with some outer margins.)

The problem is that depending on the font size and container height, some of the labels in the stack view will be clipped. Here is an example with the third label:

Third label in stack view is clipped

I have experimented with layout constraint priorities for both the stack view and the labels, but this doesn't appear to be the right approach. Instead setting the visibility of the labels works better: correct spacing between elements is maintained.

My question is then what is the right time to detect that the label's height isn't fully displayed and to hide it.

The label height is close to, but not exactly equal to the UIFont's lineHeight so there's some rounding involved that makes this a little difficult.

The biggest problem is that after a layout pass in the UIStackView's layoutSubviews the heights of the arranged subviews can be detected, but you can't hide the arranged views at that point because it causes another layout pass and recursion.

So what am I missing? :-)

Here's a test project - build for iPhone Xs in the Simulator and you'll see the same results in the screenshot above.

Solution

Tom Irving's gist below pointed me in the right direction. The trick is to enumerate the subviews after a layout pass and remove them if height requirements aren't met.

The updated project shows how to do this in DebugStackView's layoutSubviews. And yes, UIStackView is a worthy adversary.

chockenberry
  • 7,811
  • 5
  • 32
  • 41
  • Just as a heads up, looks like you might have created this project with the Xcode 12.2 beta? I'm off the beta train and needed to switch the deployment target to 12.1. – Daniel Larsen Feb 27 '19 at 20:42

1 Answers1

0

Could you act on viewDidLoad?

My intuition would be to add up the height of all visible subviews in the stack view and then hide the last if there's a problem.

In the sample you've provided I would recommend getting a CGSize with [self.firstLabel textRectForBounds:self.view.bounds limitedToNumberOfLines:0] for each visible label, making sure to take the margin between items into acount, and determining if the total height is greater than the constant height you've assigned to the stack view. If so, hide the elements that go beyond the stack view's height.

Of course, there might be more to the problem than I understand, but that would allow you to calculate before the layoutSubview pass happens.

Daniel Larsen
  • 198
  • 1
  • 11