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 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.
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.
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 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 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 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 (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.
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.
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.
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.
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.
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.
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.
// 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.
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.
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.
// 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.
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.
| Characteristic | UIFont | CTFont |
|---|---|---|
| Framework | UIKit | Core Text |
| Creation | systemFont / preferredFont / initWithName | CTFontCreateWithName / CTFontCreateWithDescriptor |
| Glyph Access | No | CTFontGetGlyphsForCharacters |
| OpenType Features | Limited (UIFontDescriptor) | Full Access |
| Variable Fonts | Via UIFontDescriptor | CTFontCopyVariation / CreateCopyWithVariation |
| Rendering | Automatic in UI Components | Core 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.
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.
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.
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
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.
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.
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”.
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.
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
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.
Read also