We recently added a lineHeight attribute to the VirtualView-ios text element to ensure more precise consistency between the two platforms when working with VirtualView-Android. After programming for Google and Stack Overflow for a while, I found that most of the information I could find was about how to implement the Line Acing property, not lineHeight. But I just want to implement a lineHeight because iOS and Android default lineSpacing is not consistent! Or need to do their own food and clothing, incidentally arranged into the article for the benefit of posterity.

About lineSpacing lineSpacing

Let’s post the default layout style of UILabel in iOS:

As you can see, the default typesetting style has very little space between the lines, which makes the text very crowded.

When this happens, the designer will ask for line spacing in order to make the text look better. A similar annotation would look something like this:

Generally speaking, since designers require lineSpacing, we would like to name lineSpacing directly. However, UILabel does not have such a directly exposed attribute. To modify lineSpacing, we need to use NSAttributedString.

NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.lineSpacing = 10;
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
[attributes setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
label.attributedText = [[NSAttributedString alloc] initWithString:label.text attributes:attributes];
Copy the code

Run it to observe the effect:

Although it looks like there is nothing wrong with our eyes, the designer’s sharp eye can tell at once that there is a gap between the design draft requirements:

How did this happen?! ? This is not what we agreed on, is it?! ? Don’t panic. Let me explain.

Implement proper line spacing

First look at the schematic:

The red area is the area where a single line of text will be drawn by default. You can see that there is some white space above and below the text (blue and red overlap). The designer wanted the blue zone to be 10pt, but if we were to set lineSpacing directly, the green zone in between the two lines of red zone would be 10pt, and that’s where the problem started.

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

After knowing the reason, the problem is easily solved. We need to subtract the built-in margin of the system when setting up lineSpacing:

NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.lineSpacing = 10 - (label.font.lineHeight - label.font.pointSize);
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
[attributes setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
label.attributedText = [[NSAttributedString alloc] initWithString:label.text attributes:attributes];
Copy the code

Observe the effect, perfect fit:

About lineHeight

If all you care about is how text looks on your iOS device, that’s all you need to see. But I wanted iOS and Android to look exactly the same, so line spacing alone wasn’t enough. The main reason, as mentioned in the introduction, is that the default white space above and below text on Android devices (the overlap of blue and red in the previous figure) is inconsistent with that on iOS devices:

On the left is an iOS device, and on the right is an Android device, you can see the same font size 20, but Android has a higher line height. Different fonts are used on different Android devices, and more differences may arise. If you don’t smooth out the difference, you can’t really achieve two-end consistency.

If the lineHeight is set to 30pt, the height of one line must be 30pt and the height of two lines must be 60pt. The text rendering will be slightly different, but the layout will be completely erased. LineHeight can also be implemented using NSAttributedString.

NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.maximumLineHeight = lineHeight;
paragraphStyle.minimumLineHeight = lineHeight;
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
[attributes setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
label.attributedText = [[NSAttributedString alloc] initWithString:label.text attributes:attributes];
Copy the code

Run it to observe the effect:

Debug mode confirms that the height of the text is correct, but why is the text displayed at the bottom of the line?

Fixed the position of text after increasing line height

To correct the position displayed in the text line, we can use the baselineOffset property to do this. This property is very useful and is often used when implementing requirements such as superscript and subscript. After debugging, it is found that the most appropriate value is (lineheight-label.font. LineHeight) / 4. The final code example is as follows:

NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.maximumLineHeight = lineHeight;
paragraphStyle.minimumLineHeight = lineHeight;
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
[attributes setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
CGFloat baselineOffset = (lineHeight - label.font.lineHeight) / 4;
[attributes setObject:@(baselineOffset) forKey:NSBaselineOffsetAttributeName];
label.attributedText = [[NSAttributedString alloc] initWithString:label.text attributes:attributes];
Copy the code

Here’s how it looks in different sizes and lines:

A problem when using both line height and line spacing

I have to say that line height and line spacing have been perfectly implemented, 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, crash is not necessarily a bug) :

The colored areas are drawn areas of the text, with lineSpacing appearing orange and lineHeight appearing green. But why would a single line text system also show a lineSpacing ah! ? That’s what this is! ?

Fortunately, we usually use line height and line spacing separately for different needs, so they don’t trigger this problem when used separately. So in the VirtualView-ios library, I will keep the logic of the high calculation consistent with the system for the moment.

conclusion

Virtualview-ios has successfully added support for the lineHeight attribute. For more details, you can go to the open source library to see the source code. Hopefully, our Tangram solution will improve and help more people develop both sides of the puzzle at once, creating a world with a jigsaw puzzle.