Align With the Text on iOS
On iOS, text-to-text alignment is simple when you are using Auto Layout.
UIView
firstBaselineAnchor
lastBaselineAnchor
UIStackView.Alignment
firstBaseline
lastBaseline
With the properties and arguments, we can cover most of the text-to-text alignment requirements.
But how about view-to-text alignment. It’s not that simple since the baseline of a non-text view is just the bottom of the view.
We need to get familiar with the font metrics.
Font Metrics
Font metrics1 |
We can get all the information above from UIFont
. The origin (0, 0)
is on the baseline, and the value of descent
is negative.
Calculation
With the information, we can align with the text as we want. Let’s see an example to align the center of a text (of course, we can align to the center of the text’s view if the text is a single-line text).
extension UIFont {
func bottomOffsetFromBaselineForVerticalCentering(targetHeight height: CGFloat) -> CGFloat {
let textHeight = ascender - descender
let offset = (textHeight - height) / 2 + descender
return offset
}
}
The calculation is easy, and we can adjust it as we need. Here is another example that may get a better (not the perfect) result for Chinese/Japanese characters.
let offset = (capHeight - height) / 2
Compare Latin letters with Kanji and Kana2 |
We can tell capHeight
may not equal to the text height, but it’s (almost?) center aligned to the text, so the calculation above should be accurate enough.
Usage
Let’s see how we can use this UIFont
extension.
Auto Layout
We have firstBaselineAnchor
and lastBaselineAnchor
, so we can align our view to the center of the first/last line of the text.
let offset = label.font
.bottomOffsetFromBaselineForVerticalCentering(targetHeight: view.height)
view.bottomAnchor
.constraint(equalTo: label.firstBaselineAnchor, constant: -offset)
.isActive = true
NSAttributedString
When we add an image to NSAttributedString
, the image will be layout above the baseline since the origin is on the baseline. However, we might want to center the image horizontally.
let textAttachment = NSTextAttachment(image: image)
let imageSize = image.size
let offset = label.font
.bottomOffsetFromBaselineForVerticalCentering(targetHeight: imageSize.height)
textAttachment.bounds = .init(origin: .init(x: 0, y: offset), size: imageSize)
attributedString.append(NSAttributedString(attachment: textAttachment))
Conclusion
Using the font metrics in UIFont
, we can cover most of the view-to-text alignment requirements with some calculation like the example above.
Those requirements may be trivial. But, there is no reason to waste the designer’s work since UIFont
has made the implementation easy enough.