How to set a custom font family as the default for an entire app in SwiftUI

Viewed 6284

In SwiftUI you get all these handy little Font convenience accessors like Font.caption, Font.title, Font.body, etc..

e.g.

VStack {
 Text("Some Title").font(Font.title)
 Text("Some Caption Text").font(Font.caption)
}

They all specify different font styles for the default Helvetica font family. I'd like to use these very helpful convenience accessors without ever using Helvetica in my app. Can I change the default font family? Or do I constantly have to apply custom fonts like e.g.:

Text("Custom Text").font(Font.custom("SourceSansPro-Regular", size: 14.0)

4 Answers

You can override the system font if you want.

    extension Font {
    
    /// Create a font with the large title text style.
    public static var largeTitle: Font {
        return Font.custom("OpenSans-Regular", size: UIFont.preferredFont(forTextStyle: .largeTitle).pointSize)
    }

    /// Create a font with the title text style.
    public static var title: Font {
        return Font.custom("OpenSans-Regular", size: UIFont.preferredFont(forTextStyle: .title1).pointSize)
    }

    /// Create a font with the headline text style.
    public static var headline: Font {
        return Font.custom("OpenSans-Regular", size: UIFont.preferredFont(forTextStyle: .headline).pointSize)
    }

    /// Create a font with the subheadline text style.
    public static var subheadline: Font {
        return Font.custom("OpenSans-Light", size: UIFont.preferredFont(forTextStyle: .subheadline).pointSize)
    }

    /// Create a font with the body text style.
    public static var body: Font {
           return Font.custom("OpenSans-Regular", size: UIFont.preferredFont(forTextStyle: .body).pointSize)
       }

    /// Create a font with the callout text style.
    public static var callout: Font {
           return Font.custom("OpenSans-Regular", size: UIFont.preferredFont(forTextStyle: .callout).pointSize)
       }

    /// Create a font with the footnote text style.
    public static var footnote: Font {
           return Font.custom("OpenSans-Regular", size: UIFont.preferredFont(forTextStyle: .footnote).pointSize)
       }

    /// Create a font with the caption text style.
    public static var caption: Font {
           return Font.custom("OpenSans-Regular", size: UIFont.preferredFont(forTextStyle: .caption1).pointSize)
       }

    public static func system(size: CGFloat, weight: Font.Weight = .regular, design: Font.Design = .default) -> Font {
        var font = "OpenSans-Regular"
        switch weight {
        case .bold: font = "OpenSans-Bold"
        case .heavy: font = "OpenSans-ExtraBold"
        case .light: font = "OpenSans-Light"
        case .medium: font = "OpenSans-Regular"
        case .semibold: font = "OpenSans-SemiBold"
        case .thin: font = "OpenSans-Light"
        case .ultraLight: font = "OpenSans-Light"
        default: break
        }
        return Font.custom(font, size: size)
    }
}

My current approach is to just recreate my own font factory:

struct MyFont {
  static let title = Font.custom("SourceSansPro-Bold", size: 24.0)
  static let body = Font.custom("SourceSansPro-Regular", size: 12.0)
}

and then use e.g. MyFont.title in place of Font.title

This is my approach:

struct Fonts {

    static func oswaldRegular(size:CGFloat) -> Font{
        return Font.custom("Oswald-Regular", size: size)
    }

    static func oswaldLight(size:CGFloat) -> Font{
        return Font.custom("Oswald-Light", size: size)
    }

    static func oswaldBold(size:CGFloat) -> Font{
        return Font.custom("Oswald-Bold", size: size)
    }

}

Here's an approach that combines some of these methods, specifically Justin's approach and Brady Murphy's Medium Article. The idea is to create a font manager that can be incorporated into a Font extension to override the default. I wish there were a compiler directive that could skip the Font extension for Previews. This will still break previews.

This func used once will grab the actual font names:

// put the following into the .onAppear of a top level view or App.init
func getCustomFontNames() {
    // get each of the font families
    for family in UIFont.familyNames.sorted() {
        let names = UIFont.fontNames(forFamilyName: family)
        // print array of names
        print("Family: \(family) Font names: \(names)")
    }
}

Font manager that can create a default but also allows for multiple fonts by calling the font family struct directly.

typealias FMgr = FontManager
struct FontManager {
    
    // dynamic font sizes
    struct dynamicSize {
        public static var largeTitle    : CGFloat   = UIFont.preferredFont(forTextStyle: .largeTitle).pointSize
        public static var title         : CGFloat   = UIFont.preferredFont(forTextStyle: .title1).pointSize
        // repeat for all the dynamic sizes
    }
    
    // App Supplied Fonts
    struct Quicksand {
        static let familyRoot   = "Quicksand"
        
        // weights
        static let heavy        = bold
        static let bold         = "\(familyRoot)-Bold"
        static let semibold     = "\(familyRoot)-SemiBold"
        static let medium       = regular
        static let regular      = "\(familyRoot)-Regular"
        static let thin         = light
        static let light        = "\(familyRoot)-Light"
        static let ultralight   = light
        
        // dynamic sizes
        static let largeTitle   : Font = Font.custom(FMgr.Quicksand.bold, size: FMgr.dynamicSize.largeTitle)
        // repeat for other sizes
        
    }

    // structs for other fonts

}
extension Font {
    
    public static var largeTitle = FMgr.Quicksand.largeTitle
    // repeat for the rest of the dynamic sizes
}
Related