← Tous les articles

SF Pro : axes variables, taille optique et contrat Dynamic Type

SF Pro, police système d’Apple depuis iOS 9 / OS X El Capitan en 2015, est une police variable dotée de trois axes (graisse, largeur, taille optique). C’est une famille dessinée qui réunit une linéale (SF Pro), une variante arrondie (SF Pro Rounded), une monospace (SF Mono), une variante compacte (SF Compact, employée sur watchOS) et une compagne à empattements (New York)1. Toute son ingénierie tourne autour du contrat Dynamic Type : les onze styles de texte de SwiftUI (.largeTitle, .title, .body, .callout, .footnote, etc.) utilisent SF Pro par défaut, et tous s’adaptent automatiquement lorsque l’utilisateur modifie sa taille de texte préférée dans les Réglages.

La plupart des applications se servent d’un ou deux de ces onze styles, ignorent Dynamic Type au-delà du comportement par défaut et ne touchent jamais aux axes variables. Résultat : une typographie qui fonctionne, mais qui ne parle pas le vocabulaire de la plateforme. Cet article parcourt la famille de polices système, les axes variables, les onze styles sémantiques, le contrat Dynamic Type, et les cas où une police personnalisée exige un travail de mise à l’échelle explicite pour y participer.

En bref

  • SF Pro Variable expose trois axes : la graisse (wght), la largeur (wdth) et la taille optique (opsz)2. La taille optique est automatique, calée sur le corps en points ; la graisse et la largeur se pilotent via Font.Weight et Font.Width dans SwiftUI.
  • La famille système comprend SF Pro (par défaut), SF Pro Rounded (éléments d’interface chaleureux), SF Mono (code, interfaces techniques), SF Compact (watchOS, contextes étroits) et New York (compagne à empattements pour la lecture éditoriale).
  • Les onze styles de texte de SwiftUI prennent en charge Dynamic Type automatiquement. .body est la valeur sûre par défaut ; de .largeTitle à .caption2, la hiérarchie de la plateforme est couverte.
  • Les polices personnalisées ne s’adaptent pas à Dynamic Type par défaut. Utilisez Font.custom("Name", size: 16, relativeTo: .body) pour inscrire la police dans la mise à l’échelle Dynamic Type3.
  • @Environment(\.dynamicTypeSize) permet à une vue d’adapter sa mise en page à la taille de texte en cours ; la plage va de .xSmall à .accessibility5 (12 tailles au total)4.

La famille de polices système

Apple livre cinq familles qui participent toutes à l’expérience typographique du système.

SF Pro

La valeur par défaut sur chaque plateforme Apple. iPhone, iPad, Mac et Vision emploient SF Pro pour le corps de texte, les titres et l’essentiel de l’interface. La police offre une large couverture linguistique (latin, grec, cyrillique, arabe, hébreu, devanagari, etc.), prend en charge nativement la mise en page de droite à gauche, et inclut des variantes dessinées pour les petites capitales, les chiffres alternatifs (alignés ou tabulaires) et les variantes stylistiques.

Accès dans SwiftUI via la police système :

Text("Hello").font(.system(.body))                    // SF Pro by default
Text("Hello").font(.system(.body, weight: .semibold)) // SF Pro Semibold
Text("Hello").font(.system(.body, design: .default))  // explicit SF Pro

SF Pro Rounded, la variante arrondie

Variante arrondie conçue pour une interface chaleureuse et engageante : complications de l’Apple Watch, anneaux d’activité de Forme, une partie des bulles directionnelles de Plans, Localiser et certaines surfaces de Santé. Les formes arrondies communiquent une douceur ; employez-les pour le ton, pas seulement pour varier le rendu visuel.

Text("Hello").font(.system(.body, design: .rounded))

SF Mono

Famille monospace pour le code, les interfaces de terminal et tout contexte où l’alignement des cellules de caractères compte. SF Mono est la police par défaut de Xcode, celle du réglage monospace de l’app Terminal, et celle de toute vue SwiftUI qui demande .monospaced.

Text("let x = 42").font(.system(.body, design: .monospaced))

SF Compact

Famille plus étroite utilisée sur watchOS, sur les surbrillances de focus de tvOS, et dans certains contextes Mac où l’espace horizontal est contraint. Les formes resserrées préservent la hauteur d’x tout en réduisant la chasse, ce qui les rend viables aux dimensions d’un cadran d’Apple Watch, où SF Pro paraîtrait à l’étroit.

Les apps watchOS emploient automatiquement SF Compact via la police système ; les apps iOS n’ont généralement pas besoin de la demander explicitement.

New York, la compagne à empattements

Famille à empattements conçue comme compagne de lecture éditoriale. New York apparaît dans Livres pour les textes longs, dans Notes pour les notes de style manuscrit, et dans SwiftUI par le design .serif.

Text("Long-form essay").font(.system(.body, design: .serif))

La compagne à empattements reste rare dans une interface applicative. Choisissez-la délibérément (un mode lecture, un passage cité, le corps d’un article) plutôt que par défaut.

Les trois axes variables

SF Pro Variable encode trois axes qui se combinent pour produire chaque glyphe :

Graisse (wght)

Neuf graisses nommées : .ultraLight, .thin, .light, .regular, .medium, .semibold, .bold, .heavy, .black. La police variable interpole en continu entre elles, mais l’API de SwiftUI expose les valeurs nommées.

Text("Heading").font(.system(.title, weight: .semibold))

La graisse porte la hiérarchie d’accentuation : .regular pour le corps, .semibold ou .bold pour les titres, .medium pour les éléments actifs d’une barre d’outils, .light pour les libellés en retrait. Les styles sémantiques (.headline, .subheadline) arrivent déjà avec des graisses par défaut cohérentes ; ne fixez une graisse explicite que lorsque le style sémantique n’a pas la bonne forme.

Largeur (wdth)

Quatre largeurs nommées dans l’API d’iOS 16+ : .compressed, .condensed, .standard, .expanded. L’axe de largeur agit sur la chasse sans toucher à la graisse ni au caractère visuel. Réservez-le aux interfaces serrées (une barre de navigation chargée) ou à la texture visuelle (un titre en corps d’affichage qui réclame plus de présence horizontale).

La largeur s’applique via le modificateur de vue .fontWidth(_:) de SwiftUI (ou en chaînant .width(_:) sur une Font) :

Text("Compressed")
    .font(.system(.title, weight: .bold))
    .fontWidth(.compressed)

La largeur est rarement le bon axe pour du corps de texte ; elle donne sa pleine mesure dans les corps d’affichage, là où la typographie fait partie du design.

Taille optique (opsz)

L’axe techniquement le plus ambitieux. Les anciennes variantes SF Text et SF Display de SF Pro forment désormais un dégradé continu encodé dans la police variable. En dessous de 20 points, le système applique les ajustements optiques de SF Text (approche plus large, traits légèrement plus épais, contrepoinçons plus ouverts). Au-delà de 20 points, SF Display prend le relais (approche resserrée, proportions affinées). La transition est fluide.

L’axe opsz est automatique. Les applications ne le pilotent pas explicitement : le système lit le corps demandé et résout la bonne taille optique. La conséquence : la typographie aux petits corps d’interface n’a pas les mêmes proportions que la même police en corps de titre, et c’est voulu. Les polices personnalisées dépourvues de taille optique paraissent justes à un corps et fausses à tous les autres ; la gestion automatique de SF Pro élimine complètement ce piège.

Les onze styles de texte

Font.TextStyle de SwiftUI définit onze styles sémantiques qui participent tous à Dynamic Type4 :

Style Taille par défaut (Large) Usage courant
.largeTitle 34 pt En-têtes de premier niveau, texte d’accroche
.title 28 pt En-têtes de section
.title2 22 pt En-têtes de sous-section
.title3 20 pt Titres secondaires
.headline 17 pt Corps accentué, titres de ligne de liste
.body 17 pt Corps de texte par défaut
.callout 16 pt Corps d’appui, légendes en contexte
.subheadline 15 pt En-têtes secondaires, métadonnées
.footnote 13 pt Petit texte d’appui
.caption 12 pt Légendes d’images, mentions légales
.caption2 11 pt Taille minimale lisible

La colonne « Taille par défaut » indique la taille au réglage Dynamic Type « Large » de l’utilisateur (le réglage système par défaut). Chaque style s’agrandit ou se réduit à mesure que l’utilisateur ajuste sa taille de texte préférée ; la hiérarchie relative, elle, reste intacte.

La bonne adoption consiste à utiliser directement les styles sémantiques :

VStack(alignment: .leading) {
    Text("Title").font(.title)
    Text("Subtitle").font(.subheadline).foregroundStyle(.secondary)
    Text("Body content here.").font(.body)
}

La hiérarchie tient à chaque réglage Dynamic Type, les conventions des HIG d’Apple sont respectées, et la typographie répond aux préférences d’accessibilité de l’utilisateur sans une ligne de code spécifique à l’application.

Le contrat Dynamic Type

Dynamic Type est le réglage de taille de texte contrôlé par l’utilisateur dans Réglages > Accessibilité > Affichage et taille du texte > Texte plus grand. La valeur circule dans l’environnement sous la forme DynamicTypeSize, avec douze valeurs allant de .xSmall à .accessibility54 :

  • Tailles standard : .xSmall, .small, .medium, .large (par défaut), .xLarge, .xxLarge, .xxxLarge
  • Tailles d’accessibilité : .accessibility1, .accessibility2, .accessibility3, .accessibility4, .accessibility5

Les applications qui emploient les styles de texte sémantiques obtiennent la mise à l’échelle sans rien faire. Celles qui veulent adapter leur mise en page à la taille courante lisent l’environnement :

struct AdaptiveLayout: View {
    @Environment(\.dynamicTypeSize) var dynamicTypeSize

    var body: some View {
        if dynamicTypeSize.isAccessibilitySize {
            VStack { content }    // stack vertically at accessibility sizes
        } else {
            HStack { content }    // horizontal at standard sizes
        }
    }
}

La propriété isAccessibilitySize couvre les cinq tailles d’accessibilité, celles où le texte devient assez grand pour faire céder la plupart des mises en page horizontales. C’est le bon réflexe pour toute mise en page dont l’équilibre dépend de la capacité du texte à tenir en largeur.

Une application peut restreindre la plage prise en charge avec le modificateur .dynamicTypeSize(_:) :

ContentView()
    .dynamicTypeSize(.large ... .accessibility3)

La contrainte borne le réglage Dynamic Type pour le sous-arbre modifié. Réservez-la aux vues dont la mise en page ne peut réellement pas absorber toute la plage ; le bon réflexe reste de prendre en charge chaque taille et d’adapter la mise en page.

Polices personnalisées et Dynamic Type

Les polices personnalisées (une police de marque livrée via le tableau UIAppFonts d’Info.plist) ne s’adaptent pas à Dynamic Type par défaut. Le correctif le plus simple tient au paramètre relativeTo: de Font.custom :

// Doesn't scale with Dynamic Type
Text("Brand").font(.custom("MyBrandFont", size: 16))

// Scales with Dynamic Type relative to body
Text("Brand").font(.custom("MyBrandFont", size: 16, relativeTo: .body))

Le paramètre relativeTo: indique à SwiftUI de mettre la police personnalisée à l’échelle selon la courbe du style body. Au réglage « Large » de l’utilisateur, le corps vaut bien les 16 pt demandés ; aux réglages supérieurs, SwiftUI applique le même multiplicateur que pour le style body.

Pour une mise à l’échelle plus fine (courbes différentes selon les tailles, gestion optique sur mesure), passez directement par UIFontMetrics d’UIKit. L’approche est plus verbeuse, mais elle autorise les ajustements par taille dont les polices personnalisées ont souvent besoin.

Quand la typographie échoue

Trois modes de défaillance méritent d’être nommés :

Des polices personnalisées à corps fixe partout. C’est l’échec d’accessibilité le plus courant dans les apps iOS : une police de marque livrée avec Font.custom("BrandFont", size: 16) (sans relativeTo:) ignore complètement Dynamic Type. Les utilisateurs qui ont choisi les tailles d’accessibilité voient le texte de marque à 16 pt pendant que le texte système grimpe à 28 pt et plus ; la hiérarchie visuelle s’inverse. Le correctif : relativeTo: sur chaque usage de police personnalisée, audité avec AccessibilityInspector au réglage Dynamic Type maximal.

Des graisses codées en dur pour accentuer. Un sous-titre stylé en .font(.body).fontWeight(.bold) est fragile : aux tailles d’accessibilité, ce corps en gras devient presque indiscernable d’un corps déjà très grand. Le style sémantique .headline gère correctement l’accentuation sur toute la plage Dynamic Type ; employez-le plutôt que body + bold.

Des mises en page qui cèdent aux tailles d’accessibilité. Une pile horizontale texte + icône + texte qui déborde à .accessibility3 est un bug de mise en page que Dynamic Type met au jour. Le correctif est le motif de mise en page adaptative avec dynamicTypeSize.isAccessibilitySize vu plus haut ; le test consiste à faire tourner l’app au réglage Dynamic Type maximal pendant la recette, pas seulement à la taille par défaut.

Ce que ce motif implique pour les apps iOS 26+

Trois enseignements.

  1. Utilisez les styles de texte sémantiques, pas des corps réglés à la main. .body, .headline, .title2, etc. embarquent Dynamic Type, la taille optique et une hiérarchie conforme à la plateforme. Un Font.system(size: 17) réglé à la main annule toutes ces fonctionnalités système et vieillit mal dès qu’Apple ajuste la progression par défaut.

  2. Passez toujours relativeTo: sur les polices personnalisées. Une police de marque livrée avec Font.custom(_, size: _, relativeTo: .body) participe à Dynamic Type. Livrée sans, elle introduit, pour chaque utilisateur concerné, une régression d’accessibilité que la recette ne détectera qu’à la taille de texte maximale.

  3. Testez les mises en page aux tailles Dynamic Type d’accessibilité. Le réglage .accessibility3 représente environ le double de la valeur Large par défaut. Des mises en page impeccables aux tailles standard cèdent régulièrement aux tailles d’accessibilité. Le correctif passe par une adaptation au niveau de la mise en page via l’environnement dynamicTypeSize, pas par un renoncement via des contraintes .dynamicTypeSize(...).

L’ensemble du cluster Écosystème Apple : les App Intents typés ; les serveurs MCP ; la question du routage ; Foundation Models ; la distinction entre LLM d’exécution et outillage ; les trois surfaces ; le motif de source unique de vérité ; deux serveurs MCP ; les points d’accroche pour le développement Apple ; les Live Activities ; l’environnement d’exécution watchOS ; les rouages de SwiftUI ; le modèle mental spatial de RealityKit ; la discipline de schéma SwiftData ; les motifs Liquid Glass ; la livraison multiplateforme ; la matrice des plateformes ; le framework Vision ; les Symbol Effects ; l’inférence Core ML ; l’API Writing Tools ; Swift Testing ; le Privacy Manifest en profondeur ; l’accessibilité comme plateforme ; ce sur quoi je refuse d’écrire. Le hub se trouve à la série Écosystème Apple. Pour un contexte plus large sur iOS et les agents IA, consultez le guide de développement d’agents iOS.

FAQ

Quelle différence entre SF Pro et SF Pro Display / SF Pro Text ?

D’iOS 9 (première livraison de SF comme police système) à iOS 16, SF existait en deux polices distinctes : SF Text en dessous de 20 pt et SF Display à partir de 20 pt, chacune avec une approche et des épaisseurs de trait réglées à la main pour sa plage de corps. SF Pro Variable réunit cette même séparation Text/Display en un axe de taille optique continu (opsz). Les deux polices ne sont plus séparées : la police variable gère la transition automatiquement, en fonction du corps demandé.

Comment obtenir des chiffres à chasse fixe dans du corps de texte ?

Utilisez .monospacedDigit() dans SwiftUI :

Text("\(score)").font(.body).monospacedDigit()

Le modificateur remplace les chiffres proportionnels de la police body par des chiffres à chasse fixe, tout en laissant le reste du texte proportionnel. À employer dans toute interface où les chiffres doivent s’aligner d’une ligne à l’autre (minuteurs, tableaux de scores, affichages de solde).

Faut-il utiliser SF Pro Rounded pour toute l’interface ?

Non. SF Pro Rounded porte un ton (chaleureux, engageant) qui convient à certains contextes et pas à d’autres. Les complications de l’app Watch, le pavé numérique du Téléphone sur iPhone et certaines surfaces de l’app Santé l’emploient. Une application de productivité, une application bancaire ou un outil de développement ne devraient généralement pas le faire. Choisissez .rounded délibérément, jamais par défaut.

Quelle plage Dynamic Type retenir pour une app iPhone ?

Par défaut, prenez en charge toutes les tailles, de .xSmall à .accessibility5. Les tailles d’accessibilité (.accessibility1 à .accessibility5) sont la manière dont les personnes malvoyantes, à mobilité réduite ou ayant d’autres besoins d’accessibilité utilisent leur iPhone. Une application qui s’y soustrait par des contraintes .dynamicTypeSize(...) les laisse en plan. Le bon réflexe est l’adaptation de la mise en page (le motif isAccessibilitySize), pas le renoncement à une partie de la plage.

Peut-on livrer une police variable personnalisée avec son application ?

Oui. Les polices variables se livrent comme n’importe quelle autre police personnalisée (ajout au tableau UIAppFonts d’Info.plist, référence via Font.custom). Pour piloter les axes variables depuis SwiftUI, passez par les API CTFont sous-jacentes de Font.custom, via UIFontDescriptor.SymbolicTraits, ou, pour un contrôle complet des axes, descendez jusqu’à CTFontCreateCopyWithAttributes avec kCTFontVariationAttribute. Le pont entre SwiftUI et les axes variables est plus verbeux que pour les polices système ; pour la plupart des applications, les polices système couvrent les besoins.

Références


  1. Apple Developer : Fonts. Panorama de la famille de polices système, avec SF Pro, SF Pro Rounded, SF Mono, SF Compact et New York. 

  2. Apple Developer : Meet the expanded San Francisco font family (WWDC 2022, session 110381). Présentation de la conception à trois axes de SF Pro Variable (graisse, largeur, taille optique). 

  3. Documentation Apple Developer : Font.custom(_:size:relativeTo:). L’initialiseur de police personnalisée qui inscrit la police dans la mise à l’échelle Dynamic Type, relativement à un style de texte choisi. 

  4. Documentation Apple Developer : DynamicTypeSize. L’énumération à douze valeurs, de .xSmall à .accessibility5, plus le prédicat isAccessibilitySize pour l’adaptation au niveau de la mise en page. 

Articles connexes

L'accessibilité dans iOS 27 : applications de lecture et contrôles personnalisés

L'accessibilité d'iOS 27 pour les applications de lecture et les contrôles personnalisés : liaison de la navigation text…

12 min de lecture

Symboles SF personnalisés : le modèle variable et les trois sources requises

SF Symbols crée 27 variantes à partir de trois dessins sources. Le modèle variable, les exigences de chemins compatibles…

10 min de lecture

Le stack d'agents du design engineer

Les design engineers ont besoin d'une infrastructure d'agents qui impose la cohérence visuelle, la discipline typographi…

14 min de lecture