How to change line height in SwiftUI

⋅ 6 min read ⋅ SwiftUI iOS 26 Text

Table of Contents

In iOS 26, SwiftUI gets a new modifier to set the line height of text, lineHeight(_:).

In this article, I will show what each option does, with pictures, so you can pick the right one.

What is line height

Line height is the distance from one baseline to the next one. The baseline is the line that letters sit on.

To set it, add .lineHeight(_:) to a Text, and pass one of the options below.

Text("Line height is the distance from one baseline to the next one.")
.font(.system(size: 20))
.lineHeight(.exact(points: 36))
A paragraph with lineHeight exact 36 points. The blue baselines are 36 points apart, with pink space between the lines.
The line height is 36 points, the distance between two blue baselines.

In the example above, we set the line height to a fixed 36 points with .exact(points:). You can also set it in other ways, for example, as a multiple of the font size.

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

The options

lineHeight(_:) takes an AttributedString.LineHeight. There are seven options.

Option Line height
.variable From the font. Same as not setting it.
.exact(points:) exactly this many points
.multiple(factor:) factor × point size
.tight 1.0 × point size
.normal 1.2 × point size
.loose 1.5 × point size
.leading(increase:) point size + increase

Here is the important part: except for .variable, every option is based on the font's point size. It doesn't look at how tall the font really is.

Let's see each of them.

.variable

.variable is the default option. If you don't set a line height, this is what SwiftUI uses.

Two copies of the same paragraph, one with no modifier and one with lineHeight variable. Both have 23.9 points between baselines and no pink space.
.variable is the default.

The gray boxes touch each other. There is no extra space between lines, because the line height is exactly the room the font needs.

.variable sets the line height based on font metrics. Every font file stores a few measurements that its designer picked. Two of them decide the line height:

  • Ascent: how far the font can go above the baseline, for tall letters like "h" and "l".
  • Descent: how far it can go below the baseline, for letters like "g" and "p".

Each font has its own ascent and descent, so the default changes with the font. Here is the same text in the system font and in Avenir Next, both at size 20 with no line height set.

The same paragraph at size 20 in the system font, with 23.9 points between baselines, and in Avenir Next, with 27.3 points between baselines.
Same size, different font, different line height.

Avenir Next has a bigger descent, so its lines are 27.3 points apart instead of 23.9.

.exact(points:)

.exact(points:) is the most straightforward one. You set the line height you want in points, and SwiftUI uses it regardless of the font size.

Text("Line height is the distance from one baseline to the next one.")
.lineHeight(.exact(points: 40))
Before: 23.9 points between baselines. After exact 40 points: 40 points, with about 16 points of pink space between lines.
40 points, no matter the font.

It won't grow with Dynamic Type, though. If the user picks a bigger text size, the letters grow, but the lines don't move apart.

You can also make it smaller than the font. SwiftUI does what you ask, and the lines overlap.

After exact 16 points on a 20-point font: the lines overlap by about 8 points, shown in purple, and the letters of one line touch the next.
16 points for a 20-point font. The lines overlap.

The last line is cut off on iOS 26

When the line height is smaller than the font, the last line doesn't fit inside the text's frame.

On iOS 26.5, SwiftUI cuts off the part that doesn't fit. On iOS 27, it draws the whole line.

iOS 26.5

On iOS 26.5, the bottom half of the word 'one.' on the last line is cut off.

iOS 27

On iOS 27, the word 'one.' on the last line is drawn in full.
The same .exact(points: 16) text on both versions.

If you support iOS 26, keep the line height at least as big as the font.

.multiple(factor:)

.multiple(factor:) sets the line height as a multiple of the font size.

line height = font size × factor

If you come from the web, this is the one you know. It works like a unitless line-height in CSS, such as line-height: 1.5, which is how most websites set it.

Text("Line height is the distance from one baseline to the next one.")
.font(.system(size: 20))
.lineHeight(.multiple(factor: 1.5))
Before: 23.9 points between baselines. After multiple factor 1.5: 30 points, with pink bands between lines and under the last line.
1.5 × 20 = 30 points.

Because it is a multiple, the space grows with the font. With a Dynamic Type font, like .body, the spacing keeps the same look at every text size.

The same factor of 1.5 on a 20-point font gives 30 points between baselines, and on a 30-point font gives 45 points.
The same factor gives 30 points at size 20 and 45 points at size 30.

.tight, .normal, and .loose

These three are presets of .multiple(factor:), with a factor already picked for you.

Text("...")
.lineHeight(.tight) // 1.0x
Text("...")
.lineHeight(.normal) // 1.2x
Text("...")
.lineHeight(.loose) // 1.5x

In the current implementation, the factors for these presets are 1.0, 1.2, and 1.5.

.tight

With tight, baselines are 20 points apart, and the lines overlap by about 4 points, shown in purple.

.normal

With normal, baselines are 24 points apart, almost the same as the default 23.9.

.loose

With loose, baselines are 30 points apart, with a pink band of about 6 points between lines.
20, 24, and 30 points for a 20-point font. Tap any image to see it at full size.
  • .tight is 20 points. The font needs 23.9, so the lines overlap (purple). Letters that go below the baseline, like "g" and "p", can touch the next line.
  • .normal is 24 points. That is almost the same as the default for the system font.
  • .loose is 30 points. You get about 6 points of pink space between lines.

So .tight is the same as .multiple(factor: 1), and .loose is the same as .multiple(factor: 1.5).

.leading(increase:)

.leading(increase:) adds a fixed number of points to the point size.

Text("Line height is the distance from one baseline to the next one.")
.font(.system(size: 20))
.lineHeight(.leading(increase: 10))
Before: 23.9 points between baselines. After leading increase 10: 30 points.
20 + 10 = 30 points, not 23.9 + 10.

Note that it starts from the point size (20), not from the default line height (23.9). So increase: 10 only adds about 6 points of visible space.

A bigger word in the line

So far, every letter used the same font. What if one word is bigger?

Text("A line with a \(Text("big").font(.system(size: 36, weight: .bold))) word in it, and more text after.")
.font(.system(size: 20))

With .variable, the line with the big word grows to make room for it. With .exact(points:), it doesn't.

Before, with variable: 27.7 points between baselines and no overlap. After, with exact 30 points: the second line is drawn on top of the first, only 14 points apart.
A 36-point word in 20-point text.

With .exact(points: 30), the two lines crash into each other.

I expected 30 points between the baselines. I measured 14. Each line is 30 points tall, but the first baseline sits at 36 points from the top, the size of the big word. So the second line ends up only 14 points below it.

.multiple(factor:) and .leading(increase:) are based on the biggest point size in the line, so they give that line more room.

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

Where the extra space goes

In all the pictures above, the pink space is below each line, including the last one. Nothing is added above the first line.

That means lineHeight makes a single line of text taller.

One line of text. With no modifier, the text is 24 points tall. With lineHeight multiple factor 2, a 16-point pink band appears under the line.
All the extra space goes under the line.

For a 20-point font, .multiple(factor: 2) makes the text 40 points tall, with 16 points of empty space under the letters. If you center this text next to an icon, the text will look like it sits too high.


Read more article about SwiftUI, iOS 26, Text, or see all available topic

Enjoy the read?

If you enjoy this article, you can subscribe to the weekly newsletter.
Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.

Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.

If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.

Become a patron Buy me a coffee Tweet Share
Previous
Prepare toolbar items for a vertical bar

Why two pieces of data matter on iPhone Duo, how the system picks a representation, and how to override it with axis behavior.

← Home