Font Descriptor: Key Concepts and Capabilities of a Font Descriptor

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

Font Descriptor is a UIFontDescriptor object in iOS and NSFontDescriptor in macOS that encapsulates all font metadata: family, typeface, size, tracking, leading, and typographic attributes. Instead of creating a font directly via UIFont(name:size:), the developer describes the desired characteristics through a descriptor, and the system selects the best match from available fonts. According to Apple Developer Documentation, UIFontDescriptor supports over 20 font attributes and is used across all iOS typography frameworks — from UIKit to Core Text and SwiftUI.

Key Takeaways

  • Font Descriptor — a descriptor object containing metadata about the family, typeface, size, and typographic attributes
  • Allows creating UIFont from a set of attributes without specifying a concrete font name
  • Supports modification of an existing font: changing size, typeface, and kerning without creating a new object
  • Used in UIKit, Core Text, and SwiftUI for font lookup and configuration
  • Provides font matching — automatic selection of the closest available typeface

What Is a Font Descriptor and Why Do You Need It

Font Descriptor is an object that describes font characteristics as a set of key-value pairs rather than a specific font file. Instead of creating a font by name, the developer specifies the family, typeface, size, and optional attributes, and the system selects the best match. This approach is critical for adaptive typography: the same descriptor can work across different iOS versions where the set of available fonts differs. According to the iOS Human Interface Guidelines, font matching via descriptors is the recommended way to work with dynamic fonts.

Without Font Descriptor, developers would have to check the availability of each font by name and manually define fallback options. The descriptor automates this process: it contains a compatibility matrix of typefaces — regular, bold, italic, bold italic — and when an exact match is not found, it returns the closest available one. System matching works at the Core Text level and is available on both iOS and macOS through the unified NSFontDescriptor API.

Descriptors are also used for custom fonts — loaded via Info.plist or added manually. If a font is registered in the system, its descriptor is created exactly the same way as for a system font. Cross-platform note: the concept of a descriptor also exists in Android through Typeface.Builder, but the iOS implementation via UIFontDescriptor provides significantly more attributes — over 20 compared to 5 in Android.

Key Properties and Attributes of Font Descriptor

UIFontDescriptor contains attributes grouped into a fontAttributes dictionary of type [UIFontDescriptor.AttributeName: Any]. Key attributes include the font family, name, size, traits, transformation matrix, optical size, and text style. According to Apple WWDC 2019 Session 227, using text styles — Dynamic Type — is mandatory for accessibility-compatible applications.

The traits attribute is a dictionary containing symbolic characteristics: weight (UIFontWeightTrait), slant (UIFontSlantTrait), width (UIFontWidthTrait), and others. These values are used by the font matcher to select a typeface. For example, UIFontWeightTrait with a value of 0.0 — regular typeface, 0.3 — bold. The system automatically maps numeric values to available font variants.

Additional attributes include featureSettings for OpenType features (ligatures, kerning, numeral proportions), visibleName for display to the user, matrix for affine font transformations, and cascadeList for cascading fallback font lists. The full list of attributes covers virtually all typography scenarios in iOS and macOS.

AttributeTypeDescription
UIFontDescriptorFamilyStringFont family (SF Pro, Helvetica Neue)
UIFontDescriptorNameStringFull font name (HelveticaNeue-Bold)
UIFontDescriptorSizeNSNumberFont size in points
UIFontDescriptorMatrixNSValueAffine transformation matrix
UIFontDescriptorTraits[String: Any]Symbolic typeface characteristics
UIFontDescriptorTextStyleStringDynamic Type style (headline, body, caption)

Creating UIFont via Font Descriptor in Swift

The primary way to work with Font Descriptor in iOS is to create a descriptor and then generate a UIFont from it. This is the preferred approach when you need a font with specific characteristics without knowing the exact name. The process consists of two steps: creating a UIFontDescriptor via an initializer and calling the font(withSize:) method on the descriptor.

swift
import UIKit

// Create font descriptor for family
let descriptor = UIFontDescriptor.init(
    fontAttributes: [
        .family: "SF Pro",
        .traits: [
            .weight: UIFont.Weight.bold
        ]
    ]
)

// Generate UIFont from descriptor
let font = descriptor.font(withSize: 17)

This approach guarantees that the system selects the best match for the bold typeface of SF Pro on the current device. Key advantage — the code does not depend on the iOS version: on iOS 13, SF Pro will be used; on earlier versions, Helvetica Neue or the default system font will be used.

To create a font with a dynamic size, use the initializer with text style and scale. Dynamic Type automatically adjusts the size to the user’s settings:

swift
// Dynamic Type descriptor
let bodyDescriptor = UIFontDescriptor
    .preferredFontDescriptor(withTextStyle: .body)

// Make font bold
let boldDescriptor = bodyDescriptor.withSymbolicTraits([.traitBold])

// Generate UIFont preserving dynamic size
let boldBodyFont = UIFont(
    descriptor: boldDescriptor!,
    size: bodyDescriptor.pointSize
)

Important: the withSymbolicTraits method returns an optional descriptor — if the requested typeface is unavailable, it returns nil. Always check the optional before use.

Modifying Attributes by Adding and Replacing

Font Descriptor supports an immutable model — each modification method returns a new descriptor with the changed attribute. This follows value semantics and makes the code predictable: the original descriptor never changes. The main modification methods are: addingAttributes for adding attributes, withFace for changing the typeface, withSize for changing the size, and withMatrix for affine transformation.

The addingAttributes method is the most flexible. It takes a dictionary of attributes and returns a new descriptor. When keys match, new values replace old ones. This allows combining changes: adding weight, changing size, and enabling ligatures in a single call. According to Apple Technical Q&A QA1682, addingAttributes is the recommended way for bulk modification via extension.

Practical use case: creating a font for monospaced text in a code editor. The developer takes the system monospaced descriptor, adds a reduced weight for thin lines, and increases tracking through featureSettings. This provides more readable code without loading a separate font.

swift
let monoDescriptor = UIFontDescriptor.monospacedDigitFontDescriptor()

// Add custom attributes
let customDescriptor = monoDescriptor.addingAttributes([
    .size: 14,
    .traits: [
        .weight: UIFont.Weight.medium
    ]
])

let monoFont = UIFont(
    descriptor: customDescriptor,
    size: 14
)

Limitation: not all attributes can be changed after creating the descriptor. For example, the font family is set once during initialization. If you need to change the family, create a new descriptor. This is related to the Core Text architecture, where the descriptor is tied to a specific font resource.

Font Matching and System Font Search

Font matching is a key feature of Font Descriptor that automatically selects the best match for the requested attributes among installed fonts. The process includes three stages: filtering (excluding incompatible fonts), ranking (sorting by degree of match), and selection (returning the best result). Matching is performed at the Core Text level — the framework underlying all iOS typography.

To find all fonts matching a descriptor, use the matchingFontDescriptors method. It returns an array of descriptors sorted by relevance. The developer can select the exact match (first element) or iterate through options for custom logic. According to the Apple Font Handling Guide, this is the only way to get a list of available typefaces for a specific family without enumerating by name.

Use case: when selecting a font in a text settings interface. The user picks a family, and the application uses matchingFontDescriptors to get all available typefaces and display them in a picker. Without matching, developers would have to hard-code a list of options that changes between iOS versions.

swift
let searchDescriptor = UIFontDescriptor.init(
    fontAttributes: [.family: "SF Mono"]
)

// Get all available typefaces
let matchingDescriptors = searchDescriptor
    .matchingFontDescriptors(withMandatoryKeys: nil)

for descriptor in matchingDescriptors {
    print(descriptor.object(forKey: .visibleName) ?? "unnamed")
}

Mandatory keys — a parameter that restricts the search. If you specify [.family], matching returns all fonts of the specified family. If you specify [.family, .traits] — only those that match both family and typeface. Using mandatory keys speeds up matching when searching for a subset of fonts.

Performance: matching is a synchronous operation taking 0.5 to 5 ms on iOS. For a single search, this is negligible, but for bulk iteration over descriptors (e.g., in a picker with 50+ fonts), it is recommended to cache results. Apple recommends performing matching once when loading the screen and saving the array of descriptors.

Integration with Core Text and CTFontDescriptor

Core Text is a low-level C-based framework for working with typography on iOS and macOS. Its counterpart to UIFontDescriptor is CTFontDescriptor, which provides the same capabilities at the Core Foundation level. CTFontDescriptor is used in Core Text APIs for creating fonts, measuring text, and rendering complex typography. UIFontDescriptor is essentially an Objective-C wrapper around CTFontDescriptor with an extended set of attributes.

Toll-free bridging between UIFontDescriptor and CTFontDescriptor allows passing descriptors between UIKit and Core Text without conversion. This is critical for applications that use custom text rendering — for example, text editors based on TextKit or code editors with syntax highlighting. Example: CTFontDescriptorCreateCopyWithAttribute creates a copy of the descriptor with an added attribute — an analogue of addingAttributes from UIFontDescriptor.

The key attribute kCTFontURLAttribute in CTFontDescriptor contains the URL of the font file. This allows determining whether a font is system or custom. For custom fonts, the URL points to the font file (.ttf or .otf); for system fonts, it points to the system font directory. Data through the descriptor is read-only; modifying the font file via URL is not supported.

For advanced typography — custom ligatures, contextual substitutions, alternative glyphs — use CTFontDescriptorCopyAttribute with the kCTFontFeatureSettingsAttribute key. This attribute contains an array of OpenType feature dictionaries that can be added through the descriptor. According to the Apple Core Text Programming Guide, combining UIFontDescriptor with Core Text gives full control over application typography.

Frequently Asked Questions

How is Font Descriptor different from UIFont?

UIFont is a ready-to-use font that renders text. Font Descriptor is a font description — a set of attributes from which a UIFont can be created. One descriptor can produce multiple UIFonts of different sizes.

Can Font Descriptor be used for custom fonts?

Yes. If the font is registered in the system (via Info.plist or Core Graphics), its descriptor is created by family or by name exactly the same way as for system fonts. Matching works for any registered fonts.

How do I get the font name from the descriptor?

Use descriptor.object(forKey: .visibleName) to get the display name, or descriptor.postscriptName for the PostScript name. The PostScript name is used when creating a font through UIFont(name:size:).

What is a cascade list in Font Descriptor?

A cascade list is an array of nested descriptors that serve as fallback fonts. If the primary font does not contain a needed character, the system sequentially checks fonts from the cascade list. It is used for emoji and CJK character support.

Does Font Descriptor work in SwiftUI?

Yes. SwiftUI supports Font Descriptor via Font(descriptor:size:). The SwiftUI wrapper accepts a UIFontDescriptor and creates a SwiftUI font, preserving all descriptor attributes — kerning, ligatures, and typeface.

Summary

  • Font Descriptor — a descriptor object with attributes of family, typeface, size, and typographic features
  • Allows creating UIFont from a set of attributes with automatic font matching
  • Supports immutable modification via addingAttributes, withSize, withSymbolicTraits
  • matchingFontDescriptors returns all available typefaces for font search and selection
  • Integrates with Core Text through toll-free bridging between UIFontDescriptor and CTFontDescriptor
  • Supports Dynamic Type through preferred text styles and scaling
  • Recommended for adaptive typography that supports all iOS versions without checking font availability

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