Fix: UILabel Text Vertically Centered Instead of Top-Aligned in UIKit

Fix: UILabel Text Vertically Centered Instead of Top-Aligned in UIKit

Written by

in

Quick answer: UILabel has no vertical alignment property, so short text sits in the vertical center of the frame. Shrink the label’s height to fit the text with sizeToFit (or intrinsic content size via Auto Layout) so the text starts at the top.

Why this happens

UILabel draws its text vertically centered inside its bounds. There is no verticalAlignment property to change this — the API simply doesn’t exist. When you give a label a fixed frame tall enough for two lines but the text only needs one, the single line floats in the middle.

The standard workaround is to make the label’s frame exactly as tall as its text. If the frame hugs the content, the text has no extra space to be centered in, so it appears top-aligned. You can do this imperatively with sizeToFit, or declaratively with Auto Layout by pinning the label’s top edge and letting its intrinsic content size drive the height.

There is one timing trap: if the label lives in a nib or storyboard inside a view controller that uses Auto Layout, calling sizeToFit in viewDidLoad won’t stick. Auto Layout sizes and positions subviews after viewDidLoad returns, and it will immediately undo your resize. The fix is to do the work in viewDidLayoutSubviews, after Auto Layout has run.

How to fix it

1. Use sizeToFit after setting text and numberOfLines

The quick imperative fix: set the text, allow multiple lines, then shrink the frame.

myLabel.text = "Some short text"
myLabel.numberOfLines = 0   // 0 means unlimited lines
myLabel.sizeToFit()

sizeToFit keeps the top-left corner fixed and shrinks the frame so the contents just fit. With the height now matching the text, the line sits at the top of where the label used to be.

If the label is in a view controller with Auto Layout, call it at the right time:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    myLabel.sizeToFit()
}

Calling it from viewDidLoad gets overwritten by the layout pass, so viewDidLayoutSubviews is the place for it.

2. Restore the width after sizeToFit for center- or right-aligned text

sizeToFit narrows the frame to fit the rightmost glyph. That’s fine for left-aligned text, but it breaks center- and right-aligned labels: the text ends up centered inside a shrunken frame, which is not what you want.

The fix is to save the original width and restore it after resizing:

myLabel.textAlignment = .center
myLabel.numberOfLines = 0

let originalWidth = myLabel.frame.width
myLabel.sizeToFit()

var frame = myLabel.frame
frame.size.width = originalWidth
myLabel.frame = frame

Now the label keeps its full width (so centering happens across the real width) but only the height shrinks, which is what produces the top alignment.

3. Let Auto Layout do it with intrinsic content size

If you’re using Auto Layout throughout, skip sizeToFit entirely. A label’s intrinsic content size already reflects its text, so you just need constraints that don’t force a taller frame:

  • Pin the label’s top edge to its superview (or to the view above it).
  • Do not pin the bottom edge to something far away, and don’t give it a fixed tall height constraint.
  • Set numberOfLines = 0 and let the height be determined by the content.
myLabel.translatesAutoresizingMaskIntoConstraints = false
myLabel.numberOfLines = 0

NSLayoutConstraint.activate([
    myLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 20),
    myLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
    myLabel.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor, constant: -20)
    // no bottom or height constraint: height comes from the text
])

With only a top anchor and no bottom/height constraint, the label’s height collapses to fit the text, and the text is effectively top-aligned.

4. Subclass UILabel and override drawText(in:)

For a reusable solution that keeps the frame at full size (useful when the label must occupy a fixed area for layout purposes), override the text drawing rect:

class TopAlignedLabel: UILabel {
    override func drawText(in rect: CGRect) {
        var newRect = rect
        let textSize = (text as NSString?)?.size(
            withAttributes: [.font: font as Any],
            limitedTo: CGSize(width: rect.width, height: .greatestFiniteMagnitude)
        ) ?? .zero
        newRect.size.height = min(textSize.height, rect.height)
        super.drawText(in: newRect)
    }
}

This draws the text at the top of the label’s bounds regardless of how tall the frame is, and works with any text alignment.

How to prevent it

  • Prefer Auto Layout with a top pin and no fixed height constraint — the label then sizes itself and the problem never appears.
  • If you use sizeToFit, always set numberOfLines = 0 first, or the label will still truncate to one line.
  • Call sizeToFit in viewDidLayoutSubviews, not viewDidLoad, when Auto Layout is active.
  • For center- or right-aligned labels, restore the original width after sizeToFit.
  • Remember that truncation line break modes (NSLineBreakByTruncatingTail, head, middle) and NSLineBreakByClipping are ignored after sizeToFit; character wrapping behaves normally.

FAQ

Why doesn’t UILabel have a vertical alignment property?

UIKit never exposed one; the text is always drawn vertically centered in the label’s bounds. The intended pattern is that the frame should match the content, which is why sizeToFit and intrinsic content size are the supported paths.

Does sizeToFit work with Auto Layout?

Not if you call it before the layout pass — Auto Layout will override the frame you set. Either call it from viewDidLayoutSubviews, or better, drop sizeToFit and use constraints that let the label’s intrinsic content size determine its height.

What about sizeThatFits instead of sizeToFit?

sizeThatFits(_:) returns the size that fits the content without changing the frame, so you can compute the height yourself and apply only that dimension — handy when you want to keep the width fixed but shrink the height.

Sources & credits

Parts of this guide are adapted from content licensed under CC BY-SA 4.0; this article is shared under the same license.