Geliştiriciler
SwiftUI Satır Yüksekliği: iOS 26 lineHeight Modifier'ı Açıklandı
iOS 26'da SwiftUI satır yüksekliği nasıl çalışır: .lineHeight preset'leri, multiple(factor:), exact ve leading değerleri, lineSpacing farkı; iPhone ve iPhone Duo'da.
AppleDuo EditörlüğüYayınlanma 6 dk okuma
Bu sayfada
- SwiftUI'da satır yüksekliği nasıl ayarlanır?
- .tight, .normal ve .loose ne anlama geliyor?
- .multiple(factor:) CSS line-height ile nasıl karşılaştırılır?
- lineHeight ile tek satırlık bir etiket neden fazla yukarıda görünüyor?
- lineHeight, lineSpacing ve leading arasındaki fark nedir?
- contentTransition(.numericText()) ile sayılar nasıl yuvarlanarak değişir?
- Satır yüksekliği iPhone Duo'da daha mı önemli?
- Önemli çıkarımlar
Tasarımcılar "17 pt, satır yüksekliği 1.5" gibi spesifikasyonlar verir ve uzun süre SwiftUI'da bunu doğrudan ifade etmenin bir yolu yoktu. Bunu satırlar arasına point ekleyen .lineSpacing ile ya da üç ince adım sunan bir fontun .leading ayarıyla yaklaşık olarak yapıyordunuz. iOS 26, gerçek bir SwiftUI satır yüksekliği modifier'ı ile bunu çözüyor.
Bu rehber lineHeight(_:) modifier'ını, kabul ettiği her değeri, eski API'lerle nasıl karşılaştırıldığını ve insanları şaşırtan tek davranışı, yani ekstra boşluğun nereye gittiğini anlatıyor. Apple'ın dokümantasyonuna, Natalia Panferova'nın Nil Coalescing'deki yazısına ve iOS geliştiricisi @sarunw'nin paylaşımlarına dayanıyor. Modifier iOS 26.0 ile yeni geldi; ayrıntılar sürümler arasında değişebilir.
SwiftUI'da satır yüksekliği nasıl ayarlanır?
.lineHeight(_:)'ı bir Text'e veya onun üzerindeki herhangi bir view'a uygulayın. Apple onu "view hiyerarşisindeki varsayılan satır yüksekliği için bir modifier" olarak tanımlıyor; yani .font gibi environment üzerinden çalışıyor:
Text(article.body)
.font(.body)
.lineHeight(.loose)
Parametre opsiyonel bir AttributedString.LineHeight; Apple'ın art arda gelen iki satırın taban çizgileri arasındaki mesafe olarak tanımladığı bir Foundation türü. Varsayılan değer nil ve Apple'a göre bu durumda SwiftUI "her bağlam için uygun bir satır yüksekliği ayarını otomatik olarak seçiyor." Bir alt ağacı bu otomatik davranışa döndürmek için nil verin.
Modifier ve eşleşen lineHeight environment değeri iOS 26.0, iPadOS 26.0, macOS 26.0, Mac Catalyst 26.0, tvOS 26.0, visionOS 26.0 ve watchOS 26.0'dan itibaren kullanılabilir. Eski sürümlere geri taşıma (back-deployment) yok; bu yüzden eski sürümleri destekliyorsanız onu #available(iOS 26.0, *) ile koruyun.
.tight, .normal ve .loose ne anlama geliyor?
AttributedString.LineHeight'ın dört statik özelliği ve üç factory metodu var. Apple'ın dokümantasyonu her birini şöyle tanımlıyor:
| Değer | Apple'ın tanımı | Dynamic Type ile ölçeklenir mi |
|---|---|---|
.tight | Sıkı algılanan bir point size katı | Evet |
.normal | Normal algılanan bir point size katı | Evet |
.loose | Gevşek algılanan bir point size katı | Evet |
.variable | Her satırdaki fontların ascent ve descent değerlerine dayanır | Evet |
.multiple(factor:) | En büyük point size çarpı factor | Evet |
.leading(increase:) | En büyük point size artı increase point | Kısmen: font büyür, ek değer büyümez |
.exact(points:) | Point cinsinden sabit bir toplam | Hayır |
Apple üç preset'in arkasındaki sayıları yayımlamıyor. @sarunw'ye göre bunlar .multiple(factor:) için kısayollar: .tight 1.0, .normal 1.2 ve .loose 1.5. Belirli bir değere ihtiyacınız varsa, kodunuz bu eşlemeye bağlı kalmasın diye katsayıyı kendiniz yazın.
Nil Coalescing'in karşılaştırması .normal ve .variable'ı varsayılandan neredeyse ayırt edilemez buldu. Fark tanımda: .variable her satırın font metriklerini izlerken, diğer değerler Apple'ın ifadesiyle "kullanılan fontların etkin ascent ve descent değerlerinden" bağımsız olarak sabittir.
.multiple(factor:) CSS line-height ile nasıl karşılaştırılır?
Web dünyasından geliyorsanız istediğiniz .multiple(factor:). @sarunw'nin ifadesiyle .lineHeight(.multiple(factor: 1.5)), CSS'teki line-height: 1.5 gibi çalışır: satır yüksekliği, font boyutu çarpı katsayıdır.
Apple'ın tanımı da aynı şeyi söylüyor, tek bir ayrıntıyla: katsayı satırdaki en büyük point size'ı çarpar; yani 17 pt'lik gövde metnini 22 pt'lik bir satır içi ifadeyle karıştıran bir satır 22 pt'yi kullanır.
Text(article.body)
.font(.body)
.lineHeight(.multiple(factor: 1.5))
.body bir text style olduğu için point size kişinin Dynamic Type ayarıyla değişir ve satır yüksekliği de onunla birlikte değişir. Nil Coalescing, multiple(factor:)'ın Dynamic Type boyutları arasında boşluğu orantılı tuttuğunu doğruluyor.
Diğer iki metot bu esnekliği kontrolle takas eder:
Text(caption)
.lineHeight(.leading(increase: 6))
Text(ticketNumber)
.lineHeight(.exact(points: 24))
.leading(increase:) en büyük point size artı sabit sayıda point'tir; bu yüzden font büyüdükçe aralık aynı kalır. .exact(points:) ise fontu tamamen yok sayar. Nil Coalescing, sabit bir değerle satırların büyük metin boyutlarında üst üste binebileceği veya kesilebileceği konusunda uyarıyor; bu yüzden onu, kullanacaksanız, yalnızca kısa ve sabit boyutlu metinlere ayırın.
lineHeight ile tek satırlık bir etiket neden fazla yukarıda görünüyor?
İşin püf noktası bu. Bir tarayıcı, CSS satır yüksekliğinin ekstra boşluğunu ikiye bölüp metnin üstüne ve altına dağıtır; CSS spesifikasyonu buna half-leading diyor. @sarunw'ye göre SwiftUI'ın .lineHeight'ı tüm ekstra boşluğu her satırın altına koyuyor, "tek satır olsa bile." Bu yüzden .multiple(factor: 2) ile tek satırlık bir etiket "fazla yukarıda duruyormuş gibi görünüyor."
Bir paragrafta bu zararsızdır: boşluk satırların arasına düşer, ki istediğiniz de buydu. Bir düğme, bir chip veya ortalanmış bir satır içindeki tek satırlık bir etikette ise metin daha uzun bir frame'in üst kısmında durur ve frame'i ortalamak metni ortalamaz.
Bununla başa çıkmanın birkaç yolu var:
- Modifier'ın kapsamını sınırlayın.
.lineHeightenvironment üzerinden aktarıldığı için onu tüm ekrana değil, paragrafText'ine uygulayın. Bir üst view onu ayarlıyorsa, bir etiketi.lineHeight(nil)ile sıfırlayın. - Taban çizgilerine göre hizalayın. Bir satırda
HStack(alignment: .firstTextBaseline)veya.lastTextBaselinekullanın; böylece etiketler frame'lerine göre değil metinlerine göre hizalanır ve bir satırın altındaki ekstra boşluk onları kaydırmaz. - Kısa metinlerde katsayıları ölçülü tutun. Büyük bir katsayı esas olarak uzun metin okumaya yardımcı olur. Başlıklar ve etiketler nadiren varsayılandan fazlasına ihtiyaç duyar.
HStack(alignment: .firstTextBaseline) {
Text(item.title)
.font(.headline)
Spacer()
Text(item.price, format: .currency(code: "USD"))
.font(.subheadline)
}
.lineHeight(nil)
Burada satır, yukarıda ayarlanmış herhangi bir satır yüksekliğini yok sayar ve iki etiket ilk taban çizgilerine göre hizalanır. İkisini de büyük Dynamic Type boyutlarıyla test edin, çünkü boşluk sorunları ilk orada ortaya çıkar.
lineHeight, lineSpacing ve leading arasındaki fark nedir?
SwiftUI'da artık satırlar arasındaki boşluğu değiştirmenin üç yolu var ve her biri farklı bir şeyi ölçüyor:
| API | İtibaren | Neyi ayarlar | Birim |
|---|---|---|---|
.lineHeight(_:) | iOS 26 | Taban çizgisinden taban çizgisine | Katsayı veya point |
.lineSpacing(_:) | iOS 13 | Bir satırın altından sonrakinin üstüne | Point |
Font.leading(_:) | iOS 14 | Fontun kendi satır aralığı | .standard, .loose, .tight |
lineSpacing(_:) satırlar arasına sabit bir aralık ekler. Bu kullanışlıdır, ancak satır yüksekliğini bir kat olarak veren bir tasarım spesifikasyonuyla örtüşmez ve aralık Dynamic Type ile büyümez. Apple'ın örneği .lineSpacing(10) kullanıyor.
Font.leading(_:), .font(.title.leading(.loose)) örneğindeki gibi bir fonta uygulanır. Apple, "leading ayarlarının kullanılabilirliğinin fonta bağlı olduğunu" ve bazı fontlarda modifier'ın hiçbir etkisi olmadığını belirtiyor. Nil Coalescing onun etkisini çok ince olarak tanımlıyor.
iOS 26 ve sonrası için yazılan yeni kodda, tasarımcıların metni tanımlama biçimine uyan .lineHeight'tır. Eski deployment target'lar için .lineSpacing'i koruyun.
contentTransition(.numericText()) ile sayılar nasıl yuvarlanarak değişir?
İlgili bir metin efekti ve skorlar, sayaçlar ve fiyatlar için sık istenen bir şey: @sarunw, .contentTransition(.numericText())'in "sayıların yeni değerlerine yuvarlanarak geçmesini sağladığını" belirtiyor. Her değişen rakam solmak yerine yeni değerine kayar.
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 }
}
}
}
}
Bunun çalışması için iki ayrıntı gerekiyor. Birincisi, Apple'ın contentTransition(_:) hakkındaki ipucu, onun yalnızca bir animasyon içinde etkili olduğunu söylüyor; bu yüzden değeri withAnimation içinde değiştirin. İkincisi, iOS 17 ile gelen numericText(value:), yönü seçmek için eski ve yeni değer arasındaki farkı kullanır. iOS 16 ile gelen eski numericText(countsDown:) ise yönü bunun yerine bir Boolean olarak alır.
.monospacedDigit() fontu, rakamlar değişirken sayının genişliğini sabit tutar. Sayılarınızın yanındaki animasyonlu semboller için SF Symbols animasyonları ve rendering modları rehberimize bakın.
Satır yüksekliği iPhone Duo'da daha mı önemli?
iPhone Duo'da iç ekran bir iPad gibi regular genişlik bildirirken dış ekran diğer iPhone'lar gibi davranır (iPhone Duo size class'ları rehberimize bakın). İç ekranı boydan boya kaplayan bir okuma view'u, dış ekrana göre çok daha uzun satırlarla sonuçlanabilir.
Uzun satırlar genellikle biraz daha fazla satır yüksekliğiyle daha iyi okunur; dış ekranda doğru görünen bir değer iç ekranda sıkışık hissettirebilir. .lineHeight bir environment değeri olduğu için onu size class'a bağlayabilirsiniz:
@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)
}
Buradaki katsayılar örnektir, Apple'ın önerisi değildir. Genişliği frame(maxWidth:) ile sınırlamak çoğu zaman herhangi bir satır yüksekliğinden daha fazla yardımcı olur, çünkü daha geniş ekranda satır uzunluğunu rahat bir düzeyde tutar.
Önemli çıkarımlar
- iOS 26, bir
AttributedString.LineHeightalan ve environment üzerinden aktarılan.lineHeight(_:)'ı ekliyor.nilotomatik anlamına gelir. .multiple(factor: 1.5), CSS'tekiline-height: 1.5gibi çalışır ve Dynamic Type ile ölçeklenir. @sarunw'ye göre.tight,.normalve.loosesırasıyla 1.0, 1.2 ve 1.5 katsayılarıdır..leading(increase:)sabit point ekler;.exact(points:)ölçeklenmez ve büyük boyutlarda kırpabilir.- Tüm ekstra boşluk her satırın altına gider, bu yüzden tek satırlık etiketler yukarıda görünür. Modifier'ın kapsamını sınırlayın ve satırları metin taban çizgilerine göre hizalayın.
.lineHeighttaban çizgisinden taban çizgisine ölçer;.lineSpacingsatırlar arasına bir aralık ekler;Font.leadingüç ince adım sunar.- Yuvarlanarak değişen sayılar için
withAnimationiçinde.contentTransition(.numericText(value:))kullanın.
Sırada, iOS 27'nin SwiftUI'da sürükleyerek sıralama ve swipe action'ları nasıl ele aldığını görün ya da iPhone Duo'nun iki ekranına zaten uyum sağlayan uygulamalar için menteşe için tasarlanmış oyunlara göz atın.
Sık sorulan sorular
SwiftUI'da satır yüksekliği nasıl ayarlanır?
iOS 26 ve sonrasında bir Text'e veya herhangi bir üst view'a .lineHeight(_:) uygulayın, örneğin .lineHeight(.multiple(factor: 1.5)). Değer environment üzerinden aşağı doğru aktarılır. nil vermek SwiftUI'ın otomatik satır yüksekliğini geri getirir.
.tight, .normal ve .loose hangi değerleri kullanır?
Apple bunları, sıkı, normal ve gevşek algılanan bir point size katına dayanan sabit satır yükseklikleri olarak belgeliyor. Geliştirici @sarunw, bunların 1.0, 1.2 ve 1.5 değerleriyle .multiple(factor:)'a eşit olduğunu bildiriyor.
SwiftUI'da lineHeight ile lineSpacing arasındaki fark nedir?
lineSpacing(_:), bir satırın altı ile sonraki satırın üstü arasına point cinsinden boşluk ekler. lineHeight(_:) ise bir taban çizgisinden diğerine olan mesafeyi, font boyutunun bir katı veya bir point değeri olarak ayarlar.
SwiftUI lineHeight Dynamic Type ile ölçeklenir mi?
multiple(factor:) ölçeklenir, çünkü point size'ı çarpar. leading(increase:) fontla birlikte büyür ama sabit sayıda point ekler. exact(points:) sabit bir toplamdır; bu yüzden ölçeklenmez ve büyük boyutlarda metni kırpabilir veya üst üste bindirebilir.
lineHeight ile tek satırlık etiketim neden fazla yukarıda görünüyor?
@sarunw'nin belirttiği gibi .lineHeight, tek satır olsa bile tüm ekstra boşluğu her satırın altına koyar. Büyük katsayıları yalnızca çok satırlı metne uygulayın ve tek satırları frame'lerini ortalamak yerine metin taban çizgilerine göre hizalayın.
Kaynaklar
- 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
Katlanmayı uygulamanızın bir parçası yapın.
iPhone Duo ile gerçekten bir şey yapan uygulama ve oyunları tanıtıyoruz. App Store bağlantısıyla sizinkini gönderin ya da diğer geliştiricilerin neler yaptığına bakın.