18

I need to align the baselines of text in UILabels. What I'm currently doing is I'm aligning the baselines of UILabels containing the text, and when the text font size in two labels is different, this results in aligned UILabels baseline but misaligned text baseline (misaligned by a small margin, but still misaligned). The labels are included in a custom UIView subclass, therefore self refers to the encompassing UIView.

here is the relevant code

[self.mySmallLabel sizeToFit];
[self.myBigLabel sizeToFit];

self.mySmallLabel.frame = CGRectMake(0,     
                                     self.bounds.size.height - self.mySmallLabel.bounds.size.height, 
                                     self.mySmallLabel.bounds.size.width, 
                                     self.mySmallLabel.bounds.size.height);

self.myBigLabel.frame = CGRectMake(self.mySmallLabel.frame.origin.x + self.mySmallLabel.bounds.size.width, 
                                   self.bounds.size.height - self.myBigLabel.bounds.size.height, 
                                   self.myBigLabel.bounds.size.width, 
                                   self.myBigLabel.bounds.size.height);
[self.mySmallLabel sizeToFit];
[self.myBigLabel sizeToFit];

This code results in the aligment in the image linked below.

Misalignemnt

As you can see, even though the UILabel baselines are aligned, the baselines of the text is misaligned by a small margin. How can I align the baselines of text dynamically (because font sizes might change at runtime)?

Guillaume Algis
  • 10,705
  • 6
  • 44
  • 72
akaralar
  • 1,103
  • 1
  • 10
  • 29

5 Answers5

24

I was using this answer in a couple of different places, but the baselines were sometimes a pixel off on Retina displays. The snippet below accounts for the screen’s scale:

[majorLabel sizeToFit];
[minorLabel sizeToFit];

CGRect changedFrame = minorLabel.frame;
changedFrame.origin.x = CGRectGetWidth(majorLabel.frame);

const CGFloat scale = [UIScreen mainScreen].scale;
const CGFloat majorLabelBaselineInSuperView = CGRectGetMaxY(majorLabel.frame) + majorLabel.font.descender;
const CGFloat minorLabelBaselineInOwnView = CGRectGetHeight(minorLabel.frame) + minorLabel.font.descender;
changedFrame.origin.y = ceil((majorLabelBaselineInSuperView - minorLabelBaselineInOwnView) * scale) / scale;

minorLabel.frame = changedFrame;
Community
  • 1
  • 1
eager
  • 627
  • 6
  • 10
  • 4
    Any version that uses auto layout? – adib Jul 30 '13 at 14:57
  • 13
    The auto layout version is easy: just align two labels by `NSLayoutAttributeBaseline` (or `NSLayoutAttributeLastBaseline` or `NSLayoutAttributeFirstBaseline`, depending on what you’re going for). – Zev Eisenberg Jan 28 '15 at 01:33
8

You can get pixel-perfect baseline alignment for any pair of UILabels by using the UIFont ascender value in a simple calculation. Here's how:

[majorLabel sizeToFit];
[minorLabel sizeToFit];

CGRect changedFrame = minorLabel.frame;
changedFrame.origin.y = ceilf(majorLabel.frame.origin.y + (majorLabel.font.ascender - minorLabel.font.ascender));
minorLabel.frame = changedFrame;

ceilf() is used because the font.ascender values may be fractional.

I've tested this on both retina and non-retina devices, with excellent results. Positioning the two labels relative to each other on the x-axis has been omitted, as your needs may vary. If you need a quick explanation of what the UIFont ascender is (plus other UIFont info) check out this clear, concise article.

John Jacecko
  • 1,870
  • 1
  • 16
  • 12
8

After iOS9. With autolayout, UILabel has an anchor called: lastBaselineAnchor. For example:

hintLabel.lastBaselineAnchor.constraint(equalTo: titleLabel.lastBaselineAnchor).isActive = true
boog
  • 1,813
  • 3
  • 18
  • 21
  • 1
    So many years... not noticing this answer. So many years swearing and calculating the font sizes differences and adjusting by eye... Thank you, good boog for opening my eyes. – Anton Ogarkov Jun 23 '22 at 18:58
3

I was looking to do this myself (just now) and found my answer on an almost identical question. It's not simple solution though, we have to do the math.

I only needed to do it with 2 different labels and I'm doing it in a subclass of UIView.

- (void)layoutSubviews {
    [majorLabel sizeToFit];
    [minorLabel sizeToFit];

    CGRect changedFrame = minorLabel.frame;
    changedFrame.origin.x = majorLabel.frame.size.width;
    changedFrame.origin.y = (majorLabel.frame.size.height + majorLabel.font.descender) - (minorLabel.frame.size.height + minorLabel.font.descender);
    minorLabel.frame = changedFrame;
}
Community
  • 1
  • 1
DBD
  • 23,075
  • 12
  • 60
  • 84
-2

With Autolayouts, its much more easier. Select the 2 labels you wish to align and goto the Align tool. Select "Bottom Edges"/ "Top Edges" / Baseline enter image description here

sonnet
  • 567
  • 4
  • 6