Introduction
Adjusting a UILabel's height to fit its text content requires two settings: numberOfLines = 0 (unlimited lines) and proper Auto Layout constraints that allow vertical expansion. With these in place, UILabel's intrinsic content size automatically calculates the required height based on the text, font, and available width. For programmatic height calculation without Auto Layout, use boundingRect(with:options:attributes:context:) or sizeThatFits(). This article covers all approaches in both UIKit and SwiftUI.
Auto Layout (Recommended)
1let label = UILabel()
2label.translatesAutoresizingMaskIntoConstraints = false
3label.numberOfLines = 0 // Required — 0 means unlimited lines
4label.lineBreakMode = .byWordWrapping
5label.text = "This is a long text that will wrap to multiple lines and the label will automatically grow taller to fit all the content."
6label.font = .systemFont(ofSize: 16)
7
8view.addSubview(label)
9
10NSLayoutConstraint.activate([
11 label.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
12 label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
13 label.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
14 // Do NOT set a height constraint — let intrinsic content size handle it
15])
With numberOfLines = 0 and leading/trailing constraints (which define the available width), UILabel calculates its own height from the text. Do not add a height constraint — that overrides the intrinsic content size.
Interface Builder Setup
11. Select the UILabel in the storyboard
22. Set "Lines" to 0 in the Attributes Inspector
33. Set "Line Break" to "Word Wrap"
44. Add constraints: leading, trailing, top (or bottom)
55. Do NOT add a height constraint
66. If Xcode shows a warning about ambiguous height, set Content Hugging
7 Priority (Vertical) to 251 and Content Compression Resistance to 750
The intrinsic content size provides the height, so you only need horizontal and one vertical positioning constraint.
Programmatic Height Calculation
1// Calculate the height a label needs for given text and width
2func heightForText(_ text: String, font: UIFont, width: CGFloat) -> CGFloat {
3 let constraintSize = CGSize(width: width, height: .greatestFiniteMagnitude)
4 let boundingBox = text.boundingRect(
5 with: constraintSize,
6 options: [.usesLineFragmentOrigin, .usesFontLeading],
7 attributes: [.font: font],
8 context: nil
9 )
10 return ceil(boundingBox.height)
11}
12
13// Usage
14let text = "A long paragraph of text that needs to wrap across multiple lines."
15let height = heightForText(text, font: .systemFont(ofSize: 16), width: 300)
16print("Required height: \(height)")
17
18// Alternative: use sizeThatFits
19let label = UILabel()
20label.numberOfLines = 0
21label.font = .systemFont(ofSize: 16)
22label.text = text
23let size = label.sizeThatFits(CGSize(width: 300, height: .greatestFiniteMagnitude))
24print("Size: \(size)") // CGSize(width: ~300, height: calculated)
boundingRect calculates the bounding rectangle for text without creating a UILabel. Use ceil() to round up to avoid clipping the last line.
In UITableViewCell
1class DynamicCell: UITableViewCell {
2
3 private let messageLabel: UILabel = {
4 let label = UILabel()
5 label.numberOfLines = 0
6 label.font = .systemFont(ofSize: 15)
7 label.translatesAutoresizingMaskIntoConstraints = false
8 return label
9 }()
10
11 override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
12 super.init(style: style, reuseIdentifier: reuseIdentifier)
13
14 contentView.addSubview(messageLabel)
15 NSLayoutConstraint.activate([
16 messageLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12),
17 messageLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
18 messageLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
19 messageLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -12),
20 ])
21 }
22
23 required init?(coder: NSCoder) { fatalError() }
24
25 func configure(with text: String) {
26 messageLabel.text = text
27 }
28}
29
30// In the UITableViewController
31override func viewDidLoad() {
32 super.viewDidLoad()
33 tableView.rowHeight = UITableView.automaticDimension
34 tableView.estimatedRowHeight = 60
35 tableView.register(DynamicCell.self, forCellReuseIdentifier: "cell")
36}
Pin the label's top and bottom to contentView with UITableView.automaticDimension for self-sizing cells.
Attributed Text Height
1let attributedText = NSMutableAttributedString(string: "Bold Title\n", attributes: [
2 .font: UIFont.boldSystemFont(ofSize: 18)
3])
4attributedText.append(NSAttributedString(string: "Regular body text that can be quite long.", attributes: [
5 .font: UIFont.systemFont(ofSize: 14)
6]))
7
8let label = UILabel()
9label.numberOfLines = 0
10label.attributedText = attributedText
11
12// Height calculation for attributed text
13let height = attributedText.boundingRect(
14 with: CGSize(width: 300, height: .greatestFiniteMagnitude),
15 options: [.usesLineFragmentOrigin, .usesFontLeading],
16 context: nil
17).height
18
19print("Attributed text height: \(ceil(height))")
SwiftUI
1import SwiftUI
2
3struct ContentView: View {
4 var body: some View {
5 VStack(alignment: .leading) {
6 // Text automatically sizes to fit content
7 Text("This is a long text that will automatically wrap to multiple lines in SwiftUI. No configuration needed.")
8 .font(.body)
9 .padding()
10
11 // Fixed width with automatic height
12 Text("Another paragraph with more text content.")
13 .frame(maxWidth: .infinity, alignment: .leading)
14 .padding()
15 }
16 }
17}
SwiftUI Text views automatically size to fit their content. No equivalent of numberOfLines = 0 is needed — multiline is the default.
Common Pitfalls
numberOfLines left at default (1): The default is 1, which clips text to a single line. Set numberOfLines = 0 for unlimited lines before expecting the label to grow.
Adding a fixed height constraint: A height constraint overrides the intrinsic content size. If you need a maximum height, use label.heightAnchor.constraint(lessThanOrEqualToConstant: 200) instead of equalToConstant.
Missing bottom constraint in self-sizing cells: For UITableView.automaticDimension to work, the label must be pinned to both the top and bottom of contentView. A missing bottom constraint makes the cell height ambiguous.
preferredMaxLayoutWidth not set: In some layout configurations (especially with horizontal stack views), UILabel needs preferredMaxLayoutWidth set to know the maximum width for wrapping. Set it to the label's actual width in layoutSubviews().
Using sizeToFit() with Auto Layout: sizeToFit() sets the frame directly, which conflicts with Auto Layout constraints. Use Auto Layout constraints and numberOfLines = 0 instead — the intrinsic content size handles everything.
Summary
Set numberOfLines = 0 and provide leading/trailing constraints — UILabel handles height automatically
Do not add a fixed height constraint — it overrides the intrinsic content size
For self-sizing table view cells, pin the label to both top and bottom of contentView and set rowHeight = UITableView.automaticDimension
Use boundingRect(with:options:attributes:context:) for programmatic height calculation
Use ceil() on calculated heights to avoid subpixel clipping
SwiftUI Text views size automatically with no configuration needed