UIFont / CTFont — What It Is, Font Types and Core Text

Author: IT Sectr Published: 2026-07-23 Reading time: 9 min

UIFont and CTFont are classes for working with fonts in iOS: UIFont from UIKit for high-level text configuration in UILabel and UITextView, CTFont from Core Text for low-level rendering with full control over glyphs and metrics. According to Apple Developer Documentation, UIFont is used in 90% of standard UI components, while CTFont is used in custom text engines and editors. Both classes represent one font but at different levels of abstraction.

Key Takeaways

  • UIFont — high-level UIKit class for working with fonts in UI components
  • CTFont — low-level Core Text class for precise control over typography
  • UIFont is created via systemFont, preferredFont or initWithName, CTFont — via CTFontCreateWithName
  • Dynamic Type — iOS adaptive font system that automatically responds to accessibility settings
  • CTFont provides direct access to glyph metrics, kerning, and OpenType tables

What Are UIFont and CTFont?

UIFont is a UIKit class representing a font for displaying text in UI components: UILabel, UITextView, UITextField. It provides a simple interface for getting system and custom fonts with specified size, weight, and style. UIFont does not allow direct control over kerning, ligatures, and OpenType features — Core Text is used for that.

CTFont is a Core Text type (CTFontRef) providing low-level access to font data. CTFont works directly with Core Graphics for rendering, supports all OpenType and TrueType features including alternate glyphs, ligatures, and variable fonts. CTFont is used internally by UIKit for rendering UIFont but is also available directly for custom drawing.

Toll-Free Bridging

UIFont and CTFont support toll-free bridging — conversion between types with no additional overhead. Casting UIFont as CTFont or CTFont as UIFont is possible in Swift and Objective-C. This allows using the high-level UIKit API for configuration and low-level Core Text for rendering without copying font data.

Font Descriptor

Both classes use UIFontDescriptor (UIKit) / CTFontDescriptor (Core Text) to describe font characteristics: name, size, weight, style, transformation matrix. A descriptor is a dictionary of attributes that can be modified to create new fonts based on existing ones. For example, add traitItalic to a descriptor and get an italic version of the same font.

UIFont — UIKit Fonts

UIFont provides several factory methods for creating fonts. systemFont(ofSize:) returns the system font San Francisco (iOS) or Helvetica (older versions). boldSystemFont, italicSystemFont — variants with specific styles. preferredFont(forTextStyle:) returns a Dynamic Type font for the given style: .headline, .body, .caption1, .footnote. These fonts automatically scale when text size settings change in the system.

For loading custom fonts, UIFont(name:size:) is used with the PostScript name of the font. The font must first be added to Info.plist and included in the app bundle. According to Apple, it is recommended to use UIFont for all standard UI components and switch to CTFont only when custom rendering is needed.

Dynamic Type

Dynamic Type is an adaptive font system introduced in iOS 7. The user selects their preferred text size in Settings — from extra small to extra large. UIFont.preferredFont automatically returns a font of the appropriate size. For proper operation, all UILabels should have adjustsFontForContentSizeCategory = true. Dynamic Type is required for accessibility compliance.

UIFontDescriptor

UIFontDescriptor allows modifying font attributes without creating a new instance from scratch. Methods addingAttributes(_:), withSymbolicTraits(_:), withFamily(_:) return a new descriptor with changed parameters. This is convenient for obtaining font variants: add traitBold to the system font and get a bold version without knowing the PostScript name.

CTFont — Core Text Fonts

CTFont (CTFontRef) is a Core Text type for low-level font work. It is created by CTFontCreateWithName, CTFontCreateWithFontDescriptor, CTFontCreateCopyWithAttributes. Each function returns a font with the specified interface, size, and optional attributes. CTFont is used in Core Graphics for rendering text on custom UIView via drawRect.

CTFont provides access to metrics not available in UIFont: CTFontGetGlyphsForCharacters returns an array of glyphs for given characters, CTFontGetAdvancesForGlyphs — the width of each glyph, CTFontGetBoundingBox — glyph boundaries. This is necessary for building custom text layouts: columns, rotation, wrapping.

Font Metrics

CTFontGetAscent, CTFontGetDescent, CTFontGetLeading return vertical font metrics. CTFontGetUnitsPerEm — number of units per em-square, used for proportion calculation. CTFontGetGlyphCount — total number of glyphs in the font. This data is critical for print typography and custom layouts where precise positioning of each character is required.

OpenType and Variable Fonts

CTFont supports OpenType features: CTFontCopyFeatures returns a dictionary of available font functions (ligatures, alternate characters, kerning), CTFontEnableFeature activates a specific option. Variable fonts are configured via CTFontCopyVariation and CTFontCreateCopyWithVariation, allowing weight, width, and slant to be changed in a continuous range.

How to Use UIFont in Code

Creating UIFont in Swift is done through one of the factory methods or direct initialization by name. The most common way is systemFont(ofSize:weight:) for system fonts or preferredFont(forTextStyle:) for Dynamic Type. For custom fonts, UIFont(name:size:) is used with the PostScript name specified.

System Font with Different Weights

UIFont.systemFont(ofSize:weight:) accepts UIFont.Weight — from .ultraLight to .black. This method returns the San Francisco system font with the corresponding weight. UIFont.monospacedSystemFont, UIFont.monospacedDigitSystemFont return monospaced fonts for code and numbers. All system fonts automatically support Dynamic Type.

swift
let regularFont = UIFont.systemFont(ofSize: 16, weight: .regular)
let boldFont = UIFont.systemFont(ofSize: 16, weight: .bold)
let headlineFont = UIFont.preferredFont(forTextStyle: .headline)
let monoFont = UIFont.monospacedSystemFont(ofSize: 14, weight: .regular)
label.font = headlineFont

Each font can be directly assigned to the font property of UILabel or used in NSAttributedString via NSFontAttributeName. System fonts are cached by iOS, so re-creating a system font does not incur overhead.

Custom Font from Bundle

To use a custom font, add the TTF or OTF file to the project bundle and specify it in Info.plist (UIAppFonts). The PostScript name of the font can be found via UIFont.familyNames and UIFont.fontNames(forFamilyName:). After registration, the font is available via UIFont(name:size:) from anywhere in the app.

swift
// Get all font family names
for family in UIFont.familyNames {
    let names = UIFont.fontNames(forFamilyName: family)
    print("\(family): \(names)")
}
// Load custom font
if let customFont = UIFont(name: "MyCustomFont-Regular", size: 18) {
    label.font = customFont
}

If the font name is specified incorrectly, UIFont(name:size:) returns nil. It is recommended to print all available font names via UIFont.familyNames and UIFont.fontNames(forFamilyName:) during debugging to ensure the font is registered correctly.

Custom Fonts with UIFont and CTFont

Loading custom fonts is possible in two ways: static registration via Info.plist (UIAppFonts) and dynamic registration via CTFontManagerRegisterGraphicsFont. The first method is simpler — the font is available immediately after app launch. The second is used for fonts downloaded from the network or on-demand resources.

Dynamic registration is useful for on-demand resources: if the app contains 20+ fonts, they can all be excluded from the initial bundle and downloaded on first use. CTFontManagerRegisterFontsForURLs registers fonts from specified URLs. After registration, the font becomes available via UIFont(name:size:) as usual.

CTFontCreateWithName for Custom Font

For custom rendering via Core Text, use CTFontCreateWithName with the PostScript name of the font. The difference with UIFont is that CTFont can be created with a transformation matrix (CTFontCreateWithNameAndOptions) — for creating skewed fonts: slant, X-axis scaling, rotation. This is not available in UIFont without using affine transforms.

swift
// Create CTFont with skew matrix
var matrix = CGAffineTransform(a: 1, b: 0, c: 0.2, d: 1, tx: 0, ty: 0)
let ctFont = CTFontCreateWithName(
    "Helvetica" as CFString,
    24, &matrix)
// Use toll-free bridging with UIFont
let uiFont = ctFont as UIFont
label.font = uiFont

The transformation matrix allows creating italic variants without a separate font file. The c parameter (skew) adds slant to characters. The matrix also allows scaling the font horizontally — creating condensed or expanded variants of one font.

Differences Between UIFont and CTFont

The main difference between UIFont and CTFont is the level of abstraction. UIFont provides a simple API for common tasks: get a system font, change size, apply to UILabel. CTFont provides full control over the font: access to glyphs, metrics, OpenType tables, and variable axes. The choice depends on the task.

CharacteristicUIFontCTFont
FrameworkUIKitCore Text
CreationsystemFont / preferredFont / initWithNameCTFontCreateWithName / CTFontCreateWithDescriptor
Glyph AccessNoCTFontGetGlyphsForCharacters
OpenType FeaturesLimited (UIFontDescriptor)Full Access
Variable FontsVia UIFontDescriptorCTFontCopyVariation / CreateCopyWithVariation
RenderingAutomatic in UI ComponentsCore Graphics, Custom

For 95% of iOS development tasks, UIFont is sufficient. Core Text with CTFont is justified when creating text editors, PDF generators, books with typographic layout, or apps with custom rendering of rare languages.

Performance and Best Practices

Working with fonts in iOS requires consideration of performance. Each unique font is loaded into memory on first access and consumes resources. San Francisco system fonts are preloaded and do not incur overhead. Custom fonts are loaded from file and cached by the system after first load.

It is recommended to limit the number of unique fonts in an app to 3-5 families. Each font takes from 50 KB to 2 MB in memory. Using different weights of one family (regular, bold, medium) is more efficient than loading several different families.

UIFont Caching

The system caches created UIFont instances, so repeated calls to UIFont.systemFont(ofSize: 16, weight: .regular) do not create a new object. However, UIFont(name:size:) for custom fonts is not always cached — it is recommended to store frequently used fonts in static properties or NSCache to avoid reloading from file.

Asynchronous Font Loading

When dynamically registering fonts via CTFontManagerRegisterGraphicsFont, the process can be asynchronous if the font file is downloaded from the network. In this case, UILabel or UITextView should display a fallback system font until loading completes. After registration, the font becomes available and the screen automatically updates the text display.

Frequently Asked Questions

Can UIFont be converted to CTFont and back?

Yes, UIFont and CTFont support toll-free bridging. Conversion is done by simple casting: UIFont as CTFont or CTFont as UIFont. No overhead or data copying occurs — both classes point to the same font object in memory.

Which is preferred for use in UIKit?

For all UI components, use UIFont. It is optimized for UIKit, supports Dynamic Type, and correctly scales when system settings change. CTFont is only justified for custom rendering via Core Graphics or when low-level font metrics access is needed.

How to find the font name for UIFont(name:size:)?

Use UIFont.familyNames for a list of families and UIFont.fontNames(forFamilyName:) for PostScript names. Call this code in debug mode and find the desired font. The PostScript name typically contains the family name and style: “SFProText-Regular”, “HelveticaNeue-Bold”.

Does UIFont support variable fonts?

Yes, through UIFontDescriptor you can configure variable axes: weight, width, slant. However, the UIFont API for variable fonts is limited compared to CTFontCopyVariation. For full control over variable axes, use Core Text and CTFontCreateCopyWithVariation.

How to properly cache UIFont?

Create a static dictionary or NSCache with a key combining name and size. For system fonts, iOS caches instances automatically, but for custom fonts created via UIFont(name:size:), it is recommended to store objects in the app cache to avoid reloading from the TTF file.

Summary

  • UIFont — high-level UIKit class for fonts in UI components with Dynamic Type
  • CTFont — low-level Core Text type for rendering and glyph access
  • UIFont is created via systemFont, preferredFont or UIFont(name:size:)
  • CTFont is created via CTFontCreateWithName or CTFontCreateWithDescriptor
  • Toll-free bridging allows free conversion between UIFont and CTFont
  • Dynamic Type is required for accessibility: use preferredFont and adjustsFontForContentSizeCategory
  • For UIKit use UIFont, for custom rendering use CTFont

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also