Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fix line spacing in custom font in SwiftUI

I am using custom font (Catamaran) and it seems like it has big space between lines in it. For example I have this code:

Text("Example text that has big space between lines")
    .lineSpacing(0)
    .padding(.horizontal)
    .font(Font.custom(FontNameManager.Catamaran.bold, size: 24.0))
    .foregroundColor(.white)
    .multilineTextAlignment(.center)

and it looks like this:

Screenshot with text example from previous code

As you can see it doesn't have zero space between lines but it still gets too much space. I even tried to set negative numbers to lineSpacing method but it doesn't help. What can I do with this? How I can fix it? In UIKit I would probably use attributed string and I think I can use UILabel as UIViewRepresentable and then I can use attributed string in SwiftUI iOS 14. Is there some easier solution which can "fix" font for any usage? Do I have to edit original .ttf file? Why there is this space between lines in this font?

Thanks for any help

like image 699
Libor Zapletal Avatar asked Jul 02 '21 18:07

Libor Zapletal


People also ask

How do I set line height in SwiftUI?

SwiftUI text does not provide a lineHeight property (line spacing is a different beast). You could try to align the 'firstTextBaseLine' to get the desired behaviour. Alternatively, use a 'UILabel' (via 'UIViewRepresentable') with an attributed string (specify line height in the paragraph style).

How do I use custom fonts in SwiftUI?

To use a custom font, add the font file that contains your licensed font to your app, and then apply the font to a text view or set it as a default font within a container view. SwiftUI's adaptive text display scales the font automtically using Dynamic Type.

How to use fonts in SwiftUI and customize text view?

Learn how to use fonts in SwiftUI and customize Text view in SwiftUI with number of Font options such as design, size, weight, color and others. SwiftUI lets you customize Text by applying a .font () modifier. The default iOS font is called San Francisco and if you don’t explicitly change it, then all of your text will have the default iOS look.

How to change the default line spacing in SwiftUI?

The reason is that the SwiftUI framework has set a default value of nil for the lineLimit modifier. You can change the value of .lineLimit to nil and see the result: Normally the default line spacing is good enough for most situations. In case you want to alter the default setting, you can adjust the line spacing by using the lineSpacing modifier.

What is uifont in SwiftUI?

The UIFont class is a programmatic interface to access font characteristics and provide the system with glyph information of the font. In SwiftUI UIFont has been simplified to a system class called Font that makes it easier to set custom fonts to be used by the application!

What is the fixed size modifier in SwiftUI?

This level of control lets us use the fixed size modifier as an alternative to layout priorities in SwiftUI. Let’s take a look at another example. In the example above, we have the horizontal stack that contains three text labels. The layout system can’t render all of them without truncating because labels have pretty big font sizes.


Video Answer


2 Answers

SwiftUI might use values of hhea (Horizontal header table) to set the Text box height. In your case, Catamaran has an ascender of 1100 and a descender of 540. The box height will be calculated as the sum of these two values: 540 + 1100 = 1640. And the UPM (Units per Em) of the font is default 1000. Which means in SwiftUI, when .font(.custom(..., size: 1000)) is set, each line of Text() will have a frame whose height is 1640.

Text Box Height

In terms of .lineSpacing(), SwiftUI doesn't set the value for spacing between baselines, but spacing between two boxes instead. If you want to have no spacing between two boxes in the example below, unfortunately setting .lineSpacing() to -(1640-1000) = -640 is not allowed (negative values not acceptable).

Set vs. Expected Line Spacing

UPDATE: An UIViewRepresentable Method

However, you can use UILabel instead to reduce line height:

struct CustomText: UIViewRepresentable {
    let text: String
    let font: UIFont
    
    func makeUIView(context: Context) -> UILabel {
        let label = UILabel()
        
        label.font = font
        label.numberOfLines = 0
        
        let attributedString = NSMutableAttributedString(string: text)
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.lineHeightMultiple = 0.6  // <- Reduce lineHeight with a <1 factor
        
        attributedString.addAttribute(NSAttributedString.Key.paragraphStyle,
                                      value: paragraphStyle,
                                      range: NSMakeRange(0, attributedString.length))
        
        label.attributedText = attributedString
        
        return label
    }
    
    func updateUIView(_ uiView: UILabel, context: Context) { }
}

Usage:

CustomText(text: "Foggy Days\nGo Nowhere",
           font: UIFont(name: "Catamaran", size: 1000)!)
like image 91
Toto Minai Avatar answered Oct 18 '22 18:10

Toto Minai


.leading seems like a thing you need (and is typographically the correct way to talk about 'line spacing').

Use:

Font.system(size: 16, weight: .regular, design: .rounded)
.leading(.tight)
like image 1
Tomáš Kafka Avatar answered Oct 18 '22 18:10

Tomáš Kafka