3

I'm using TextStyles in my app to support dynamic fonts. I have the challenge to change the Font for each TextStyle. So for example the TextStyle.body should be MyAwesomeBODYFont and the TextStyle.headline should be MyAwesomeHeadlineFont. And this for the entire app. Setting the font for the whole app won't work because I need several Fonts for the different styles.

Is it possible to override these TextStyles somehow with custom fonts for the entire app and not for each label separately?

What I tried:

Setting the font for the appearance proxy of UILabel in general works fine:

let labelAppearance = UILabel.appearance()
let fontMetrics = UIFontMetrics(forTextStyle: .body)
labelAppearance.font = fontMetrics.scaledFont(for: myAwesomeBodyFont)

But this overrides all labels not matter what TextStyle they use.

After that I tried to check for the TextStyle but it crashes with a nil pointer exception for the UILabel.appearance().font or does not even go into the if-block.

let labelAppearance = UILabel.appearance()
if let textStyle = labelAppearance.font.fontDescriptor.object(forKey: UIFontDescriptor.AttributeName.textStyle) as? UIFont.TextStyle {
    // this would be the place to check for the TextStyle and use the corresponding font

    let fontMetrics = UIFontMetrics(forTextStyle: textStyle)
    labelAppearance.font = fontMetrics.scaledFont(for: mayAwesomeBodyFont)
}

Because the appearance of UILabel does not have a font set.

palme
  • 2,499
  • 2
  • 21
  • 38
  • You cannot directly "set" a custom font for Text Styles. I'd suggest searching for `dynamic type custom font` - you'll find many resources. Here is one place to start: https://useyourloaf.com/blog/using-a-custom-font-with-dynamic-type/ – DonMag Mar 04 '19 at 18:31
  • Thanks for the search recommendation! I actually searched for the exact term already and used this article :) I specified my question a bit. – palme Mar 05 '19 at 09:13
  • TypographyKit is a third party module that can help with this problem. – Warren Burton Mar 05 '19 at 09:17
  • TypographyKit sounded interesting when I found it but it also sets this for every single label resp. UI component. Thanks for your comment anyway, I looked into the Framework again but could not find their solution for that specific problem. – palme Mar 05 '19 at 09:31

2 Answers2

2

You cannot directly "set" a custom font for Text Styles. You can get font size for text style and then could use custom family.

let systemDynamicFontDescriptor = UIFontDescriptor.preferredFontDescriptorWithTextStyle(UIFontTextStyleBody)
let size = systemDynamicFontDescriptor.pointSize
let font = UIFont(name: MyAwesomeBODYFont, size: size)

For iOS 11+ There is scaledFont()

You can make this font variable static and could use it everywhere in app.

You could check it out this solution too: https://stackoverflow.com/a/42235227/4846167

Aaina Jain
  • 353
  • 1
  • 8
  • Thanks @Aaiana for your answer. Maybe I wasn't very specific in my question. I edit my post. I'm looking for a general solution where I don't need to set this for each label. – palme Mar 05 '19 at 09:15
  • Got your point. Maybe check it out this http://curtclifton.net/fonts-with-style. Article is old but can give some idea. – Aaina Jain Mar 05 '19 at 11:26
1

I ended up creating a subclass of UILabel and let all my labels inherit from it. This way you can set the class in InterfaceBuilder and/or create the custom class in code.

This is the DynamicCustomFontLabel class:

import UIKit

class DynamicCustomFontLabel: UILabel {

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!

        initCustomFont()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)

        initCustomFont()
    }

    private func initCustomFont() {
        if let textStyle = font.fontDescriptor.object(forKey: UIFontDescriptor.AttributeName.textStyle) as? UIFont.TextStyle {
            let fontMetrics = UIFontMetrics(forTextStyle: textStyle)
            var customFont: UIFont?

            switch textStyle {
            case .body:
                customFont = UIFont(name: "MyAwesomeBODYFont", size: 21)

            case .headline:
                customFont = UIFont(name: "MyAwesomeHeadlineFont", size: 48)

            // all other cases...

            default:
                return
            }

            guard let font = customFont else {
                fatalError("Failed to load a custom font! Make sure the font file is included in the project and the font is added to the Info.plist.")
            }

            self.font = fontMetrics.scaledFont(for: font)
        }
    }
}
palme
  • 2,499
  • 2
  • 21
  • 38