SF Pro: Variable Axes, Optical Sizing, And The Dynamic Type Contract
TITLE : SF Pro : axes variables, taille optique et le contrat Dynamic Type
DESCRIPTION : La police système d’Apple est livrée avec trois axes variables et une taille optique continue. Le vocabulaire qui fait fonctionner la typographie sur les plateformes Apple.
BODY:
SF Pro, la 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), une famille conçue qui inclut une sans serif (SF Pro), une variante arrondie (SF Pro Rounded), une monospace (SF Mono), une variante compacte (SF Compact, utilisée sur watchOS) et une compagne serif (New York)1. La police est conçue autour du contrat Dynamic Type : les onze styles de texte de SwiftUI (.largeTitle, .title, .body, .callout, .footnote, etc.) utilisent tous SF Pro par défaut, et ils s’adaptent automatiquement lorsque l’utilisateur modifie sa taille de texte préférée dans les Réglages.
La plupart des applications utilisent un ou deux de ces onze styles, ignorent Dynamic Type au-delà du comportement par défaut et ne touchent jamais aux axes variables. Le résultat est 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ù les polices personnalisées nécessitent un travail de mise à l’échelle explicite pour participer.
TL;DR
- SF Pro Variable expose trois axes : graisse (
wght), largeur (wdth) et taille optique (opsz)2. La taille optique est automatique selon la taille en points ; la graisse et la largeur sont adressables viaFont.WeightetFont.Widthde SwiftUI. - La famille système comprend SF Pro (par défaut), SF Pro Rounded (éléments d’interface conviviaux), SF Mono (code, interface technique), SF Compact (watchOS, contextes étroits) et New York (compagne serif pour la lecture éditoriale).
- Les onze styles de texte de SwiftUI prennent automatiquement en charge Dynamic Type.
.bodyest la valeur par défaut sûre ; de.largeTitleà.caption2, ils couvrent la hiérarchie de la plateforme. - Les polices personnalisées ne s’adaptent pas à Dynamic Type par défaut. Utilisez
Font.custom("Name", size: 16, relativeTo: .body)pour intégrer la police à la mise à l’échelle Dynamic Type3. @Environment(\.dynamicTypeSize)permet à une vue d’adapter sa mise en page à la taille de texte actuelle ; la plage de valeurs va de.xSmallà.accessibility5(12 tailles au total)4.
La famille de polices système
Apple propose cinq familles qui participent toutes à l’expérience de la police système.
SF Pro
La police par défaut sur toutes les plateformes Apple. iPhone, iPad, Mac et Vision utilisent SF Pro pour le corps de texte, les titres et la majeure partie de l’interface. La police a 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 conçues pour les petites capitales, les chiffres alternatifs (alignés ou tabulaires) et les variantes stylistiques.
Accédez-y 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
Une variante arrondie conçue pour une interface conviviale et accessible : complications de l’Apple Watch, anneaux Fitness, certaines bulles directionnelles de Plans, Localiser et certaines surfaces de Santé. Les formes arrondies communiquent la douceur ; utilisez-les pour le ton, pas seulement pour la variété visuelle.
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 est important. SF Mono est la police de Xcode par défaut, dans le réglage monospace de l’application Terminal et dans toute vue SwiftUI qui demande .monospaced.
Text("let x = 42").font(.system(.body, design: .monospaced))
SF Compact
Une famille plus étroite utilisée sur watchOS, sur les surbrillances de focus tvOS et dans certains contextes Mac où l’espace horizontal est contraint. Les formes plus étroites préservent la hauteur d’x tout en réduisant l’avance horizontale, ce qui les rend adaptées aux dimensions du cadran de l’Apple Watch où SF Pro paraîtrait à l’étroit.
Les applications watchOS utilisent automatiquement SF Compact via la police système ; les applications iOS n’ont généralement pas besoin de la demander explicitement.
New York
Une famille serif conçue comme compagne pour la lecture éditoriale. New York apparaît dans Livres pour les textes longs, dans Notes pour les notes de style manuscrit et dans SwiftUI via le design .serif.
Text("Long-form essay").font(.system(.body, design: .serif))
La compagne serif est rare dans l’interface des applications. Utilisez-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 continuellement entre elles, mais l’TERM_18 de SwiftUI expose les valeurs nommées.
Text("Heading").font(.system(.title, weight: .semibold))
La graisse communique la hiérarchie d’emphase : .regular pour le corps, .semibold ou .bold pour les titres, .medium pour les éléments actifs de la barre d’outils, .light pour les libellés mis en retrait. Les styles sémantiques (.headline, .subheadline) sont livrés avec des graisses par défaut sensées ; n’ayez recours aux graisses explicites que lorsque le style sémantique n’est pas la bonne forme.
Largeur (wdth)
Quatre largeurs nommées dans l’TERM_18 iOS 16+ : .compressed, .condensed, .standard, .expanded. L’axe de largeur affecte l’avance horizontale sans changer la graisse ni le caractère visuel. Utilisez-le pour une interface serrée (une barre de navigation avec de nombreux éléments) ou pour la texture visuelle (un titre de taille d’affichage qui veut plus de présence horizontale).
La largeur s’applique via le modificateur de vue .fontWidth(_:) de SwiftUI (ou enchaîné sur une Font via .width(_:)) :
Text("Compressed")
.font(.system(.title, weight: .bold))
.fontWidth(.compressed)
La largeur est rarement le bon axe pour le corps de texte ; elle fonctionne bien dans les tailles d’affichage où la typographie fait partie du design.
Taille optique (opsz)
L’axe techniquement ambitieux. Les anciennes variantes SF Text et SF Display de SF Pro sont désormais un dégradé continu encodé dans la police variable. Aux tailles inférieures à 20 points, le système applique les ajustements optiques SF Text (espacement de lettres plus large, tracés légèrement plus épais, contre-formes plus ouvertes). Au-dessus de 20 points, SF Display prend le relais (espacement plus serré, proportions affinées). La transition est fluide.
L’axe opsz est automatique. Les applications ne l’adressent pas explicitement ; le système lit la taille en points demandée et résout vers le bon dimensionnement optique. L’implication : la typographie aux petites tailles d’interface a des proportions différentes de la même police aux tailles de titre, et c’est voulu. Les polices personnalisées qui n’ont pas de taille optique semblent correctes à une taille et incorrectes à d’autres ; la gestion automatique de SF Pro évite entièrement 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 hero |
.title |
28 pt | En-têtes de section |
.title2 |
22 pt | En-têtes de sous-section |
.title3 |
20 pt | Titres mineurs |
.headline |
17 pt | Corps mis en avant, titres de lignes de liste |
.body |
17 pt | Corps de texte par défaut |
.callout |
16 pt | Corps de soutien, légendes en contexte |
.subheadline |
15 pt | En-têtes secondaires, métadonnées |
.footnote |
13 pt | Petit texte de soutien |
.caption |
12 pt | Légendes d’images, petits caractères |
.caption2 |
11 pt | Texte minimum lisible |
La colonne « Taille par défaut » indique la taille au réglage Dynamic Type « Large » de l’utilisateur (le défaut système). Chaque style augmente ou diminue selon les ajustements de la taille de texte préférée par l’utilisateur ; la hiérarchie relative reste intacte.
La bonne approche d’adoption est d’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 est préservée à chaque réglage Dynamic Type, les conventions HIG d’Apple sont respectées et la typographie répond aux préférences d’accessibilité de l’utilisateur sans 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 > Taille du texte plus grande. La valeur transite par 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 utilisent les styles de texte sémantiques bénéficient gratuitement de la mise à l’échelle. Les applications qui souhaitent adapter la mise en page à la taille actuelle 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é (où le texte est suffisamment grand pour que les mises en page horizontales se brisent souvent). Le motif est le bon pour toute mise en page qui dépend de l’ajustement horizontal du texte.
Les applications peuvent contraindre la plage prise en charge avec le modificateur .dynamicTypeSize(_:) :
ContentView()
.dynamicTypeSize(.large ... .accessibility3)
La contrainte limite le réglage Dynamic Type pour le sous-arbre modifié. Utilisez-la lorsque la mise en page d’une vue ne peut véritablement pas accommoder toute la plage ; le bon défaut est de prendre en charge toutes les tailles et d’adapter la mise en page à la place.
Polices personnalisées et Dynamic Type
Les polices personnalisées (une police de marque livrée via le tableau UIAppFonts du Info.plist) ne s’adaptent pas à Dynamic Type par défaut. La solution la plus simple est le 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 à l’échelle la police personnalisée en utilisant la courbe de mise à l’échelle du style body. La taille de la police au réglage « Large » de l’utilisateur est les 16pt demandés ; aux réglages plus grands, SwiftUI applique le même multiplicateur que celui qu’utiliserait le style body.
Pour une mise à l’échelle plus sophistiquée (différentes courbes à différentes tailles, gestion optique personnalisée), utilisez directement UIFontMetrics d’UIKit. Le motif est plus verbeux mais prend en charge les ajustements par taille dont les polices personnalisées ont souvent besoin.
Quand la typographie échoue
Trois modes d’échec qu’il vaut la peine de nommer :
Polices personnalisées de taille fixe partout. L’échec d’accessibilité d’application iOS le plus courant : une police de marque livrée avec Font.custom("BrandFont", size: 16) (sans relativeTo:) ignore entièrement Dynamic Type. Les utilisateurs avec des tailles de texte d’accessibilité voient le texte de marque à 16pt tandis que le texte système se met à l’échelle à 28pt+ ; la hiérarchie visuelle s’inverse. La solution est relativeTo: sur chaque utilisation de police personnalisée, vérifiée via AccessibilityInspector au réglage Dynamic Type maximal.
Graisses codées en dur pour l’emphase. Un sous-titre stylisé .font(.body).fontWeight(.bold) est fragile : aux tailles d’accessibilité, le body en gras devient presque indiscernable d’un body qui est déjà grand. Le style sémantique .headline gère correctement l’emphase à travers la plage Dynamic Type ; utilisez-le au lieu de body+bold.
Mises en page qui se brisent aux tailles d’accessibilité. Une pile horizontale de texte + icône + texte qui déborde à .accessibility3 est un bug de mise en page que Dynamic Type expose. La solution est le motif de mise en page adaptative dynamicTypeSize.isAccessibilitySize ci-dessus ; le test consiste à exécuter l’application au Dynamic Type maximal pendant le QA, pas seulement à la taille par défaut.
Ce que ce motif signifie pour les applications iOS 26+
Trois enseignements.
-
Utilisez les styles de texte sémantiques, pas des tailles en points ajustées à la main.
.body,.headline,.title2, etc. portent Dynamic Type, la taille optique et la hiérarchie correcte de la plateforme. UnFont.system(size: 17)ajusté à la main défait toutes les fonctionnalités système et vieillit mal lorsqu’Apple ajuste la rampe par défaut. -
Passez toujours
relativeTo:sur les polices personnalisées. Une police de marque livrée avecFont.custom(_, size: _, relativeTo: .body)participe à Dynamic Type. Une police de marque livrée sans cela est une régression d’accessibilité par utilisateur que le QA ne détectera qu’à la taille de texte maximale. -
Testez les mises en page aux tailles Dynamic Type d’accessibilité. Le réglage
.accessibility3est environ 2 fois la valeur Large par défaut. Les mises en page qui semblent correctes aux tailles standard se brisent régulièrement aux tailles d’accessibilité. La solution est l’adaptation au niveau de la mise en page via l’environnementdynamicTypeSize, et non le retrait via les contraintes.dynamicTypeSize(...).
L’ensemble du cluster Apple Ecosystem : App Intents typés ; les serveurs TERM_17 ; la question du routage ; Foundation Models ; la distinction TERM_23 runtime vs outillage ; les trois surfaces ; le motif source unique de vérité ; Deux serveurs TERM_17 ; les hooks pour le développement Apple ; les Live Activities ; le runtime watchOS ; les internals SwiftUI ; le modèle mental spatial de RealityKit ; la discipline de schéma SwiftData ; les motifs Liquid Glass ; la livraison multiplateforme ; la matrice de plateformes ; le framework Vision ; les Symbol Effects ; l’inférence Core ML ; l’TERM_18 Writing Tools ; Swift Testing ; le Privacy Manifest ; l’accessibilité comme plateforme ; ce sur quoi je refuse d’écrire. Le hub se trouve dans la série Apple Ecosystem. Pour un contexte iOS-avec-agents-IA plus large, consultez le guide iOS Agent Development.
FAQ
Quelle est la différence entre SF Pro et SF Pro Display / SF Pro Text ?
D’iOS 9 (lorsque SF a été livré pour la première fois comme police système) à iOS 16, SF était livré sous forme de deux polices distinctes : SF Text pour les tailles inférieures à 20pt et SF Display pour les tailles de 20pt et plus, chacune avec un espacement de lettres et des graisses de tracés ajustés à la main pour sa plage de tailles. SF Pro Variable consolide la même séparation Text/Display en un axe de taille optique continu (opsz). Les deux polices ne sont plus distinctes ; la police variable gère automatiquement la transition selon la taille en points demandée.
Comment obtenir des chiffres monospace dans le 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 monospace tout en gardant le reste du texte proportionnel. Utilisez-le pour toute interface où les chiffres doivent s’aligner d’une ligne à l’autre (chronomètres, tableaux de scores, affichages de soldes).
Devrais-je utiliser SF Pro Rounded pour toute l’interface ?
Non. SF Pro Rounded porte un ton (convivial, accessible) qui convient à certains contextes et pas à d’autres. Les complications de l’application Watch, le pavé numérique du Téléphone de l’iPhone et certaines surfaces de l’application Santé l’utilisent. Une application de productivité, une application bancaire ou un outil de développement ne devraient généralement pas l’utiliser. Recourez à .rounded délibérément, pas par défaut.
Quelle est la bonne plage Dynamic Type pour une application iPhone ?
Par défaut, prenez en charge toutes les tailles de .xSmall à .accessibility5. Les tailles d’accessibilité (.accessibility1 à .accessibility5) sont la façon dont les utilisateurs malvoyants, ayant des difficultés motrices ou d’autres besoins d’accessibilité utilisent l’iPhone. Une application qui se retire via les contraintes .dynamicTypeSize(...) échoue à servir ces utilisateurs. La bonne approche est l’adaptation de la mise en page (le motif isAccessibilitySize), pas le retrait de la plage de tailles.
Puis-je livrer une police variable personnalisée avec mon application ?
Oui. Les polices variables se livrent comme toute autre police personnalisée (ajout au tableau UIAppFonts du Info.plist, référence par Font.custom). Pour adresser les axes variables depuis SwiftUI, utilisez les __TERM_18__s CTFont sous-jacents de Font.custom via UIFontDescriptor.SymbolicTraits ou, pour un contrôle complet des axes, descendez à CTFontCreateCopyWithAttributes avec kCTFontVariationAttribute. Le pont de SwiftUI vers les axes variables est plus verbeux que pour les polices système ; pour la plupart des applications, les polices système couvrent les cas.
Références
-
Apple Developer : Fonts. Présentation de la famille de polices système, comprenant SF Pro, SF Pro Rounded, SF Mono, SF Compact et New York. ↩
-
Apple Developer : Meet the expanded San Francisco font family (session WWDC 2022 110381). L’introduction de la conception à trois axes de SF Pro Variable (graisse, largeur, taille optique). ↩
-
Documentation Apple Developer :
Font.custom(_:size:relativeTo:). L’initialiseur de police personnalisée qui intègre la police à la mise à l’échelle Dynamic Type relative à un style de texte choisi. ↩ -
Documentation Apple Developer :
DynamicTypeSize. L’énumération à douze valeurs de.xSmallà.accessibility5, plus le prédicatisAccessibilitySizepour l’adaptation au niveau de la mise en page. ↩↩↩