Développeurs
Hauteur de ligne en SwiftUI : le modificateur lineHeight d'iOS 26 expliqué
La hauteur de ligne en SwiftUI sous iOS 26 : préréglages de .lineHeight, multiple(factor:), valeurs exact et leading, face à lineSpacing, sur iPhone et iPhone Duo.
Par la rédaction AppleDuoPublié le 7 min de lecture
Sur cette page
- Comment régler la hauteur de ligne en SwiftUI ?
- Que signifient .tight, .normal et .loose ?
- Comment .multiple(factor:) se compare-t-il au line-height de CSS ?
- Pourquoi un libellé d'une ligne semble-t-il trop haut avec lineHeight ?
- Quelle est la différence entre lineHeight, lineSpacing et leading ?
- Comment faire défiler des chiffres avec contentTransition(.numericText()) ?
- La hauteur de ligne compte-t-elle davantage sur iPhone Duo ?
- À retenir
Les designers livrent des spécifications du type « 17 pt, hauteur de ligne 1,5 », et pendant longtemps SwiftUI n'offrait aucun moyen direct de l'exprimer. On s'en approchait avec .lineSpacing, qui ajoute des points entre les lignes, ou avec le .leading d'une police, qui ne propose que trois paliers discrets. iOS 26 règle le problème avec un vrai modificateur de hauteur de ligne en SwiftUI.
Ce guide présente le modificateur lineHeight(_:), toutes les valeurs qu'il accepte, sa comparaison avec les anciennes API, et le comportement qui surprend : l'endroit où va l'espace supplémentaire. Il s'appuie sur la documentation d'Apple, l'article de Natalia Panferova sur Nil Coalescing et les publications du développeur iOS @sarunw. Le modificateur est nouveau dans iOS 26.0, et certains détails peuvent changer d'une version à l'autre.
Comment régler la hauteur de ligne en SwiftUI ?
Appliquez .lineHeight(_:) à un Text, ou à n'importe quelle vue située au-dessus. Apple le décrit comme un modificateur de la hauteur de ligne par défaut dans la hiérarchie de vues : il agit donc par l'environnement, comme .font :
Text(article.body)
.font(.body)
.lineHeight(.loose)
Le paramètre est un AttributedString.LineHeight optionnel, un type Foundation qu'Apple définit comme la distance entre les lignes de base de deux lignes successives. La valeur par défaut est nil ; dans ce cas, selon Apple, SwiftUI choisit automatiquement une hauteur de ligne adaptée à chaque contexte. Passez nil pour rendre ce comportement automatique à une sous-hiérarchie.
Le modificateur et la valeur d'environnement lineHeight correspondante sont disponibles à partir d'iOS 26.0, iPadOS 26.0, macOS 26.0, Mac Catalyst 26.0, tvOS 26.0, visionOS 26.0 et watchOS 26.0. Il n'y a pas de rétroportage : protégez l'appel avec #available(iOS 26.0, *) si vous prenez en charge des versions plus anciennes.
Que signifient .tight, .normal et .loose ?
AttributedString.LineHeight propose quatre propriétés statiques et trois méthodes de fabrique. La documentation d'Apple décrit chacune :
| Valeur | Description d'Apple | Suit Dynamic Type |
|---|---|---|
.tight | Multiple de la taille en points perçu comme serré | Oui |
.normal | Multiple de la taille en points perçu comme normal | Oui |
.loose | Multiple de la taille en points perçu comme aéré | Oui |
.variable | Basée sur l'ascendante et la descendante des polices de chaque ligne | Oui |
.multiple(factor:) | Plus grande taille en points multipliée par factor | Oui |
.leading(increase:) | Plus grande taille en points plus increase points | En partie : la police grandit, l'ajout non |
.exact(points:) | Un total fixe en points | Non |
Apple ne publie pas les valeurs derrière les trois préréglages. Selon @sarunw, ce sont des raccourcis pour .multiple(factor:) : .tight vaut 1.0, .normal 1.2 et .loose 1.5. Si vous avez besoin d'une valeur précise, écrivez vous-même le facteur pour que votre code ne dépende pas de cette correspondance.
La comparaison de Nil Coalescing a trouvé .normal et .variable presque impossibles à distinguer de la valeur par défaut. La différence tient à la définition : .variable suit les métriques de la police de chaque ligne, alors que les autres valeurs restent constantes quelles que soient, selon Apple, l'ascendante et la descendante effectives des polices utilisées.
Comment .multiple(factor:) se compare-t-il au line-height de CSS ?
Si vous venez du web, c'est .multiple(factor:) qu'il vous faut. Comme le dit @sarunw, .lineHeight(.multiple(factor: 1.5)) fonctionne comme le line-height: 1.5 de CSS : la hauteur de ligne est la taille de police multipliée par le facteur.
La définition d'Apple dit la même chose, avec une précision : le facteur multiplie la plus grande taille en points de la ligne, si bien qu'une ligne mêlant un corps de 17 pt et un passage de 22 pt utilise 22 pt.
Text(article.body)
.font(.body)
.lineHeight(.multiple(factor: 1.5))
Comme .body est un style de texte, la taille en points change selon le réglage Dynamic Type de l'utilisateur, et la hauteur de ligne suit. Nil Coalescing confirme que multiple(factor:) garde un espacement proportionnel à toutes les tailles Dynamic Type.
Les deux autres méthodes échangent cette souplesse contre du contrôle :
Text(caption)
.lineHeight(.leading(increase: 6))
Text(ticketNumber)
.lineHeight(.exact(points: 24))
.leading(increase:) correspond à la plus grande taille en points plus un nombre fixe de points : l'écart reste le même quand la police grandit. .exact(points:) ignore complètement la police. Nil Coalescing avertit qu'avec une valeur exacte, les lignes peuvent se chevaucher ou être coupées aux grandes tailles de texte : réservez-la, si besoin, à des textes courts de taille fixe.
Pourquoi un libellé d'une ligne semble-t-il trop haut avec lineHeight ?
Voilà le piège. Un navigateur répartit l'espace supplémentaire d'un line-height CSS pour moitié au-dessus et pour moitié au-dessous du texte ; la spécification CSS appelle cela le half-leading. Selon @sarunw, le .lineHeight de SwiftUI place tout l'espace supplémentaire sous chaque ligne, « même lorsqu'il n'y en a qu'une ». Un libellé d'une ligne avec .multiple(factor: 2) semble donc trop haut.
Dans un paragraphe, c'est sans conséquence : l'espace se retrouve entre les lignes, ce qui est le but. Dans un libellé d'une ligne à l'intérieur d'un bouton, d'une pastille ou d'une rangée centrée, le texte se place en haut d'un cadre plus grand, et centrer le cadre ne centre pas le texte.
Quelques façons de gérer ce cas :
- Limitez la portée du modificateur. Comme
.lineHeightse transmet par l'environnement, appliquez-le auTextdu paragraphe, pas à tout l'écran. Si un parent le définit, réinitialisez un libellé avec.lineHeight(nil). - Alignez sur les lignes de base. Dans une rangée, utilisez
HStack(alignment: .firstTextBaseline)ou.lastTextBaselinepour que les libellés s'alignent sur leur texte et non sur leur cadre, et que l'espace sous une ligne ne les décale pas. - Gardez des facteurs modestes pour les textes courts. Un grand facteur aide surtout la lecture de textes longs. Les titres et les libellés ont rarement besoin de plus que la valeur par défaut.
HStack(alignment: .firstTextBaseline) {
Text(item.title)
.font(.headline)
Spacer()
Text(item.price, format: .currency(code: "USD"))
.font(.subheadline)
}
.lineHeight(nil)
Ici, la rangée ignore toute hauteur de ligne définie plus haut, et les deux libellés s'alignent sur leur première ligne de base. Testez-les aux grandes tailles Dynamic Type, car c'est là que les problèmes d'espacement apparaissent en premier.
Quelle est la différence entre lineHeight, lineSpacing et leading ?
SwiftUI propose désormais trois façons de modifier l'espace entre les lignes, et elles ne mesurent pas la même chose :
| API | Depuis | Ce qu'elle définit | Unité |
|---|---|---|---|
.lineHeight(_:) | iOS 26 | De ligne de base à ligne de base | Facteur ou points |
.lineSpacing(_:) | iOS 13 | Du bas d'une ligne au haut de la suivante | Points |
Font.leading(_:) | iOS 14 | L'interligne propre à la police | .standard, .loose, .tight |
lineSpacing(_:) ajoute un écart fixe entre les lignes. C'est utile, mais cela ne correspond pas à une spécification qui exprime la hauteur de ligne comme un multiple, et l'écart ne grandit pas avec Dynamic Type. L'exemple d'Apple utilise .lineSpacing(10).
Font.leading(_:) s'applique à une police, comme dans .font(.title.leading(.loose)). Apple note que la disponibilité des ajustements d'interligne dépend de la police, et pour certaines polices le modificateur n'a aucun effet. Nil Coalescing juge son effet très discret.
Pour du nouveau code sur iOS 26 et ultérieur, .lineHeight est celui qui correspond à la façon dont les designers spécifient le texte. Gardez .lineSpacing pour les cibles de déploiement plus anciennes.
Comment faire défiler des chiffres avec contentTransition(.numericText()) ?
Un effet de texte voisin, souvent demandé pour les scores, les compteurs et les prix : @sarunw souligne que .contentTransition(.numericText()) fait défiler les chiffres jusqu'à leur nouvelle valeur. Au lieu d'un fondu, chaque chiffre qui change glisse vers sa nouvelle valeur.
struct ScoreView: View {
@State private var score = 0
var body: some View {
VStack {
Text(score, format: .number)
.font(.largeTitle.monospacedDigit())
.contentTransition(.numericText(value: Double(score)))
Button("Add 10") {
withAnimation { score += 10 }
}
}
}
}
Deux détails le font fonctionner. D'abord, la note d'Apple sur contentTransition(_:) précise qu'il n'a d'effet qu'au sein d'une animation : modifiez donc la valeur dans withAnimation. Ensuite, numericText(value:), disponible depuis iOS 17, utilise la différence entre l'ancienne et la nouvelle valeur pour choisir le sens. L'ancien numericText(countsDown:), apparu dans iOS 16, prend le sens sous forme de booléen.
La police .monospacedDigit() garde une largeur constante pendant que les chiffres changent. Pour des symboles animés à côté de vos chiffres, consultez notre guide des animations et modes de rendu des SF Symbols.
La hauteur de ligne compte-t-elle davantage sur iPhone Duo ?
Sur iPhone Duo, l'écran intérieur renvoie une largeur regular, comme un iPad, tandis que l'écran extérieur se comporte comme les autres iPhone (consultez notre guide des size classes de l'iPhone Duo). Une vue de lecture qui occupe l'écran intérieur peut ainsi avoir des lignes bien plus longues que sur l'écran extérieur.
Les lignes longues se lisent généralement mieux avec un peu plus de hauteur de ligne, et une valeur qui convient sur l'écran extérieur peut sembler serrée sur l'écran intérieur. Comme .lineHeight est une valeur d'environnement, vous pouvez la lier à la size class :
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
var body: some View {
Text(article.body)
.font(.body)
.lineHeight(.multiple(factor: horizontalSizeClass == .regular ? 1.5 : 1.3))
.frame(maxWidth: 680)
}
Les facteurs utilisés ici sont des exemples, pas des recommandations d'Apple. Limiter la largeur avec frame(maxWidth:) aide souvent plus que n'importe quelle hauteur de ligne, car cela garde une longueur de ligne confortable sur l'écran plus large.
À retenir
- iOS 26 ajoute
.lineHeight(_:), qui accepte unAttributedString.LineHeightet se transmet par l'environnement.nilsignifie automatique. .multiple(factor: 1.5)fonctionne comme leline-height: 1.5de CSS et suit Dynamic Type. Selon @sarunw,.tight,.normalet.loosecorrespondent aux facteurs 1.0, 1.2 et 1.5..leading(increase:)ajoute un nombre fixe de points ;.exact(points:)ne s'adapte pas et peut rogner le texte aux grandes tailles.- Tout l'espace supplémentaire va sous chaque ligne, si bien que les libellés d'une ligne semblent trop hauts. Limitez la portée du modificateur et alignez les rangées sur les lignes de base.
.lineHeightmesure de ligne de base à ligne de base ;.lineSpacingajoute un écart entre les lignes ;Font.leadingpropose trois paliers discrets.- Pour faire défiler des chiffres, utilisez
.contentTransition(.numericText(value:))danswithAnimation.
Ensuite, découvrez comment iOS 27 gère le glisser pour réorganiser et les actions de balayage en SwiftUI, ou parcourez les jeux conçus pour le pli pour voir des apps qui s'adaptent déjà aux deux écrans de l'iPhone Duo.
Questions fréquentes
Comment régler la hauteur de ligne en SwiftUI ?
Sur iOS 26 et ultérieur, appliquez .lineHeight(_:) à un Text ou à n'importe quelle vue parente, par exemple .lineHeight(.multiple(factor: 1.5)). La valeur se transmet par l'environnement. Passer nil rétablit la hauteur de ligne automatique de SwiftUI.
Quelles valeurs utilisent .tight, .normal et .loose ?
Apple les décrit comme des hauteurs de ligne constantes basées sur un multiple de la taille en points perçu comme serré, normal et aéré. Le développeur @sarunw indique qu'elles équivalent à .multiple(factor:) avec 1.0, 1.2 et 1.5.
Quelle est la différence entre lineHeight et lineSpacing en SwiftUI ?
lineSpacing(_:) ajoute un espace, en points, entre le bas d'une ligne et le haut de la suivante. lineHeight(_:) définit la distance d'une ligne de base à la suivante, soit comme un multiple de la taille de police, soit comme une valeur en points.
Le lineHeight de SwiftUI suit-il Dynamic Type ?
multiple(factor:) le suit, car il multiplie la taille en points. leading(increase:) grandit avec la police mais ajoute un nombre fixe de points. exact(points:) est un total fixe : il ne s'adapte pas et peut rogner ou faire se chevaucher le texte aux grandes tailles.
Pourquoi mon libellé d'une ligne semble-t-il trop haut avec lineHeight ?
Comme le note @sarunw, .lineHeight place tout l'espace supplémentaire sous chaque ligne, même lorsqu'il n'y en a qu'une. N'appliquez de grands facteurs qu'au texte sur plusieurs lignes, et alignez les lignes uniques sur leur ligne de base plutôt que de centrer leur cadre.
Sources
- lineHeight(_:) · Apple Developer Documentation
- AttributedString.LineHeight · Apple Developer Documentation
- multiple(factor:) · Apple Developer Documentation
- exact(points:) · Apple Developer Documentation
- leading(increase:) · Apple Developer Documentation
- lineSpacing(_:) · Apple Developer Documentation
- Font.Leading · Apple Developer Documentation
- numericText(value:) · Apple Developer Documentation
- contentTransition(_:) · Apple Developer Documentation
- iOS 26 adds a .lineHeight modifier to SwiftUI · @sarunw on X
- .lineHeight presets .tight, .normal, .loose = .multiple(factor:) 1.0, 1.2, 1.5 · @sarunw on X
- .lineHeight(.multiple(factor: 1.5)) works like CSS line-height: 1.5 · @sarunw on X
- .lineHeight puts all extra space below each line · @sarunw on X
- .contentTransition(.numericText()) makes numbers roll to their new value · @sarunw on X
- Adjusting line height in SwiftUI on iOS 26 · Nil Coalescing
- CSS 2.2: Visual formatting model details (line-height) · W3C
Faites du pli une partie de votre app.
Nous présentons les apps et les jeux qui exploitent vraiment l’iPhone Duo. Envoyez-nous la vôtre avec un lien vers l’App Store, ou découvrez ce que d’autres ont créé.