A horizontal stack view can align text based views on the first or last baseline of the text. Unfortunately the stack view ends up with an incorrect height when the first text view is not the view that extends furthest above/below the baseline. Adding some extra constraints works around the problem.
Last updated: Aug 13, 2020: This bug still exists in iOS 13.
Baseline Alignment of Text
Here is what I expect to happen. I have a horizontal stack view with three text labels. The second text label uses a font that is twice the size of the other labels. There is a small amount of spacing between the views and I have set the alignment to
let stackView = UIStackView(arrangedSubviews: [label1, label2, label3]) stackView.spacing = 8.0 stackView.alignment = .firstBaseline
I centered the stack view below the top layout guide of a view controller. I also added a yellow background view to the stack view so we can see its frame. This is how I would expect it to look:
The three text labels are baseline aligned with the stack view height set by the height of the second (tallest above baseline) text label. The text labels should be within the bounds of the stack view.
The text labels align on their first baselines but the stack view pins its top edge to the top of the first (and smaller) text label. This causes the taller second text label to extend above the bounds of the stack view and in this case to end up under the navigation bar.
It works fine when the first label uses the largest font (or the labels are all the same font size):
Looking at the configuration of the stack view with the view debugger it would appear to be missing constraints from the top of the stack view to the top of the 2nd and 3rd labels. The first text label has a constraint with the top of the stack view as follows:
UISV-canvas-connection: stackView.top <= label.top @ 1000
This constraint makes sure the stack view top is never over the top of the label. This constraint is missing for the other two labels. A workaround is to add this constraint to each label:
NSLayoutConstraint.activate([ stackView.topAnchor.constraint(lessThanOrEqualTo: label2.topAnchor), stackView.topAnchor.constraint(lessThanOrEqualTo: label3.topAnchor) ])
Since this is a less than or equal to constraint the top of the stack view ends up positioned equal to the top of the label that extends the highest above the baseline.
If you are using a last baseline alignment replace topAnchor with bottomAnchor and use a greater than or equal to relation:
stackView.alignment = .lastBaseline NSLayoutConstraint.activate([ stackView.bottomAnchor.constraint(greaterThanOrEqualTo: label2.bottomAnchor), stackView.bottomAnchor.constraint(greaterThanOrEqualTo: label3.bottomAnchor) ])
Unless I am missing something I assume this is a bug. I have posted a sample Xcode project reproducing the problem and bug report on Open Radar if you want to duplicate: