How to correctly implement line spacing and line height in iOS

How to correctly implement line spacing and line height in iOS

Recently, I am going to add a lineHeight attribute to the text element of VirtualView-iOS so that it can more accurately ensure the consistency of the two platforms when working with VirtualView-Android. After programming on Google and Stack Overflow for a while, I found that most of the information I can find is about how to implement the lineSpacing attribute, not lineHeight. But I want to implement lineHeight because the default lineSpacing of iOS and Android is inconsistent! I still need to do it myself and organize it into an article for the benefit of future generations.

About line spacing lineSpacing

First, here is the default layout style of UILabel in iOS:

As you can see, in the default typesetting style, the line spacing between texts is very small, making the text appear very crowded.

At this time, the designer will put forward the requirement of line spacing, hoping to make the text display more beautiful. Similar annotations will be like this:

Generally speaking, since the designer requires line spacing, we can just set lineSpacing directly. However, UILabel does not have such a directly exposed attribute. If we want to modify lineSpacing, we need to use NSAttributedString to do it. The schematic code is as follows:

  1. NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
  2. paragraphStyle.lineSpacing = 10;
  3. NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
  4. [attributes setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
  5. label.attributedText = [[NSAttributedString alloc] initWithString:label.text attributes:attributes];

Run and observe the effect:

Although it seems to be fine to our eyes, the designer's sharp eyes can see at a glance that there is a gap with the design requirements:

How could this happen? This is different from what we agreed on, right? Don’t panic, I will explain it in detail.

Correct line spacing

First look at the schematic diagram:

The red area is the area that a single line of text will occupy by default. You can see that there is some white space above and below the text (the overlapping part of blue and red). The designer wants the blue area to be 10pt high, but if we directly set lineSpacing, the height of the green area between the two red lines will be set to 10pt, which is the root of the problem.

So what is the height of this red area? The answer is label.font.lineHeight, which is the original line height of drawing a single line of text using the specified font.

After knowing the reason, the problem is easy to solve. We need to subtract the system's own margin when setting lineSpacing:

  1. NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
  2. paragraphStyle.lineSpacing = 10 - (label.font.lineHeight - label.font.pointSize);
  3. NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
  4. [attributes setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
  5. label.attributedText = [[NSAttributedString alloc] initWithString:label.text attributes:attributes];

Observe the effect, the best fit:

About line height lineHeight

If you only care about the text display effect on iOS devices, then this is enough. But what I need is that iOS and Android display the same effect, so the line spacing alone cannot meet the needs. The main reason is also mentioned in the introduction. The default white space above and below the text on Android devices (the overlapping part of blue and red in the previous section) is inconsistent with that on iOS devices:

The left side shows an iOS device, and the right side shows an Android device. You can see that both display a size 20 font, but the line height of the Android device is a bit higher. Different Android devices use different fonts, and there may be more differences. If you don't find a way to smooth out these differences, you can't truly achieve consistency on both ends.

At this time, we can set lineHeight to make each line of text have the same height. When lineHeight is set to 30pt, the height of one line of text must be 30pt, and the height of two lines of text must be 60pt. Although there will be slight differences in text rendering, the differences in layout will be completely erased. lineHeight can also be achieved with the help of NSAttributedString, as shown in the code:

  1. NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
  2. paragraphStyle.maximumLineHeight = lineHeight;
  3. paragraphStyle.minimumLineHeight = lineHeight;
  4. NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
  5. [attributes setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
  6. label.attributedText = [[NSAttributedString alloc] initWithString:label.text attributes:attributes];

Run and observe the effect:

I confirmed in debug mode that the text height is correct, but why is the text displayed at the bottom of the line?

Corrected the position of text after increasing line height

To correct the position of the text in the line, we can use the baselineOffset property. This property is very useful and is often used when implementing requirements such as superscripts and subscripts. After debugging, it is found that the most suitable value is (lineHeight - label.font.lineHeight) / 4 (I haven't figured out why it is divided by 4 instead of 2. I hope that the old driver who knows can give me some advice). The final code example is as follows:

  1. NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
  2. paragraphStyle.maximumLineHeight = lineHeight;
  3. paragraphStyle.minimumLineHeight = lineHeight;
  4. NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
  5. [attributes setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
  6. CGFloat baselineOffset = (lineHeight - label.font.lineHeight) / 4;
  7. [attributes setObject:@(baselineOffset) forKey:NSBaselineOffsetAttributeName];
  8. label.attributedText = [[NSAttributedString alloc] initWithString:label.text attributes:attributes];

Here are the display effects at different font sizes and line heights:

A problem when using line height and line spacing at the same time

I have to say that we have already implemented line height and line spacing perfectly, but when I tried to use them at the same time, I found a bug in iOS (of course it may also be a feature, after all, not crashing is not necessarily a bug):

The colored areas are the text drawing areas, the orange area is lineSpacing, and the green area is lineHeight. But why does the single-line text system also display a lineSpacing? This is ridiculous!

Fortunately, we usually use line height and line spacing independently for different needs, and they will not trigger this problem when used separately. So in the VirtualView-iOS library, I temporarily keep the height calculation logic consistent with the system.

Summarize

So far, we have successfully added support for the lineHeight property to VirtualView-iOS. For more implementation details, you can go to the open source library to view the source code. I hope our Tangram solution can be more perfect, helping more people to develop and use both ends at the same time, and use a piece of Tangram to create a world.

<<:  WeChat and Alipay jointly promote “contactless payment” at highway toll stations. Can they compete with ETC?

>>:  Live Broadcast with Live Broadcasting X WeChat Mini Program

Recommend

How much does it cost to produce a Baoshan pet mini program?

How much is the quote for pet production in Baosh...

MBaaS in-depth review: Five clouds for building mobile apps

【51CTO translation】 MBaaS (Mobile Backend as a Se...

Operators must know: content operation plans and strategic ideas!

1. Becoming a content operator from scratch ( Int...

Come and see these high-tech trucks that are no longer just big

When it comes to technology in the automotive fie...

The truth behind computer and mobile phone freezing: cosmic ray interference

Why do our computers and mobile phones freeze dur...

8 combinations of targeted information flow promotion methods!

Many people don’t know how to set up the targetin...

In-depth brand communication strategy—penetrate!

There are two things that left a deep impression ...

Why is iPhone 6 still selling well despite being criticized?

It has been a while since the iPhone 6 was offici...

How to invest in Baidu bidding and CPC in 2021?

We all know that now o cpc has become the mainstr...

June 1st Children's Day marketing plan!

The 2021 Children's Day is coming soon. As th...

Zhang Fan: Strange Creatures in the Black Water

"This is the kingdom of plankton. Anemones, ...