Mac uygulamanızdaki bir etiketi genişletmeden ölçmek için incelemek istediğiniz görünüme onGeometryChange ekleyin. Daha eski bir dağıtım hedefi için arka planına bir GeometryReader yerleştirin. Değiştiricinin konumu, hangi genişliği okuyacağınızı belirler.
Bu adım adım kılavuzda, çevresindeki düzeni sonuçtan bağımsız tutarken dolgu da dâhil sentetik bir etiket ölçülüyor. Ayrıca etiketin mevcut genişliğini ideal genişliğinden ve kapsayıcısının genişliğinden nasıl ayırt edeceğiniz açıklanıyor.
Geometry reader düzenin genişlemesine neden olur?
SwiftUI düzeni, öneriler ve yanıtlar üzerinden işler: üst görünüm alan önerir, alt görünümler de hesaplanan boyutlarını bildirir. Bir çerçeve eklemek, hiyerarşiye başka bir görünüm ekler; bu nedenle etiketin ve çerçevesinin genişlikleri farklı olabilir. Apple bu süreci Basit bir görünümü düzenleme başlıklı yazısında açıklar.
GeometryReader, esnek bir tercih edilen boyuta sahip kapsayıcıdır. Bu nedenle kompakt bir etiketi bunun içine sarmak, üst görünüme sunulan düzen katılımcısını değiştirir. Okuyucunun kapsayıcısını ölçersiniz; bu kapsayıcı metnin ihtiyaç duyduğundan çok daha fazla yer kaplayabilir. Apple’ın GeometryReader belgelerine bakın.
Gözlemleme için ölçümü mevcut görünüme ekleyin. Arka plan okuyucusu gerektiğinde konumu önemlidir: Apple, arka planın eklendiği görünümün boyutunu aldığını belirtir. Böylece ölçüm alanını etiket belirler. Görünümünüze arka plan ekleme başlıklı yazıya bakın.
Bir uygulama seçmeden önce arayüzünüzün hangi soruyu yanıtlaması gerektiğine karar verin:
| Sorunuz | Uygun yaklaşım |
|---|---|
| Bu etiket mevcut düzeninde ne kadar geniş? | Etiketin geometrisini gözlemleyin. |
| Etiketin çevresindeki dolgulu rozet ne kadar geniş? | Dolgudan sonra gözlemleyin. |
| Etiketin bulunduğu satır ne kadar geniş? | Satırı veya çerçevesini gözlemleyin. |
| Metnin tamamı ne kadar geniş olmayı tercih eder? | İdeal boyutlandırmayı ayrıca inceleyin. |
| Birkaç sunumdan hangisi sığar? | ViewThatFits kullanmayı değerlendirin. |
| Birden fazla alt görünüm birlikte nasıl ölçülüp konumlandırılmalı? | Özel bir Layout kullanmayı değerlendirin. |
Bu ayrım yaygın bir hatayı önler: kısıtlanmış ve muhtemelen kırpılmış bir etiketin genişliğini, metnin tümünü göstermek için gereken alan sanmayı.
macOS 13 ve sonraki sürümlerde mevcut genişliği ölçme
Apple’ın güncel belgeleri, onGeometryChange(for:of:action:) için tek değerli eylem aşırı yüklemesinin macOS 13 ve sonrasında kullanılabildiğini belirtiyor. Bu yöntem, geometri vekilini eşitlenebilir bir değere dönüştürür ve değer değiştiğinde bir eylem çağırır. Bu API’yi içeren bir SDK kullanın ve burada gösterilen tek parametreli eylemi tercih edin. onGeometryChange(for:of:action:) başlıklı belgeye bakın.
Xcode’da SwiftUI kullanan ve dağıtım hedefi macOS 13 veya sonrası olan bir macOS uygulaması projesi oluşturun. Oluşturulan uygulama ve içerik görünümü bildirimlerini bu eksiksiz programla değiştirin; hedefte yalnızca bir @main uygulama bildirimi bırakın. Örnek yalnızca yerel, sentetik etiketler kullanır.
import SwiftUI
import Foundation
@main
struct WidthDemoApp: App {
var body: some Scene {
WindowGroup {
WidthDemoView()
}
}
}
struct WidthDemoView: View {
@State private var measuredWidth: CGFloat = 0
@State private var offeredWidth: Double = 260
@State private var showLongLabel = false
private var label: String {
showLongLabel
? "Sample collection of blue circles"
: "Blue circles"
}
var body: some View {
VStack(alignment: .leading, spacing: 16) {
Text(label)
.font(.system(size: 16))
.lineLimit(1)
.padding(.horizontal, 12)
.padding(.vertical, 8)
.background(Color.blue.opacity(0.12))
.onGeometryChange(for: CGFloat.self) { proxy in
proxy.size.width
} action: { newWidth in
measuredWidth = newWidth
}
.frame(
width: CGFloat(offeredWidth),
alignment: .leading
)
Text(String(
format: "Measured badge: %.1f pt",
Double(measuredWidth)
))
.monospacedDigit()
.frame(width: 340, alignment: .leading)
Slider(value: $offeredWidth, in: 120...340)
Button("Change sample label") {
showLongLabel.toggle()
}
}
.frame(width: 360, alignment: .leading)
.padding(20)
}
}
Gözlemci proxy.size.width değerini okur; Apple, GeometryProxy.size değerini kapsayıcı görünümün boyutu olarak tanımlar. Burada gözlemlenen görünüm metin ve dolguyu içerirken, sonraki çerçeve bağımsız olarak denetlenen bir genişlik önerisi sağlar. GeometryProxy.size başlıklı belgeye bakın.
Ölçüm, ayrı bir bilgi alanını günceller. Ölçülen rozetin genişliğini ayarlamaz. Sabit demo alanı ve bilgi alanı genişliği, tanılama metninin çevresindeki yığının boyutunu değiştirmesine izin vermeden sonucu incelemeyi kolaylaştırır.
Yalnızca CGFloat döndürmek, gözlemi genişliğe odaklar: yalnızca yükseklik değişirse dönüştürülen değer değişmez. Apple, geometri sık değişebileceğinden yalnızca ihtiyaç duyduğunuz veriye dönüştürmenizi önerir. onGeometryChange(for:of:action:) başlıklı belgeye bakın.
Bu örnekler, alınan API bildirimleriyle karşılaştırılarak incelendi. Mevcut ortam Swift araç zinciri olmayan Linux olduğu için burada derlenip çalıştırılmadılar; arayüzü bir Mac’te Xcode’da derleyip inceleyin.
Ölçüm değiştiricisi nereye eklenmeli?
Değiştirici zincirini, art arda sarılmış görünümler olarak okuyun. Ölçüm, dâhil etmek istediğiniz boyutu etkileyen son değiştiricinin hemen ardından gelmelidir. Apple’ın düzen açıklamasında çerçevelerin görünümleri nasıl sardığı ve dolgunun içerik için mevcut alanı nasıl değiştirdiği anlatılır. Basit bir görünümü düzenleme başlıklı yazıya bakın.
| Örnekte ölçümün konumu | Gözlemlediği şey |
|---|---|
| Her iki dolgu değiştiricisinden önce | Metin görünümünün mevcut düzen genişliği |
| Her iki dolgu değiştiricisinden sonra | Dolgulu rozetin mevcut genişliği |
| Açık genişlik çerçevesinden sonra | Dış çerçevenin genişliği |
Çevreleyen VStack üzerinde |
Yığının genişliği |
Örnek bir hesaplama olarak, bir metin görünümünün mevcut öneri altında 96 nokta bildirdiğini varsayalım. Yatay kenarların her birine 12 nokta eklemek dolgulu genişliği 120 noktaya çıkarır. 260 noktalık dış çerçeve ise başka, ayrı bir ölçümdür. Bu sayılar değiştirici sınırlarını örnekler; örnek dizeden ölçülmüş sonuçlar değildir.
Beklenmedik bir değeri araştırmak için gözlemciyle değiştirici zincirinde aynı konuma geçici olarak bir kenarlık yerleştirin. Bu sınırı hedeflediğiniz yerle karşılaştırın. Tüm satıra eklenen bir okuyucu, görünür etiketin yalnızca küçük bir bölümünü kapladığı durumda bile satırın genişliğini doğru biçimde bildirebilir.
macOS 12 hedefi için arka plan okuyucusu kullanma
macOS 12 dağıtım hedefinde genişliği bir tercih aracılığıyla iletmek için arka planda GeometryReader kullanın. Aşağıda kullanılan sondaki kapatma ifadeli arka plan API’si macOS 12’den itibaren kullanılabilir. Apple, değiştirilen görünümün düzene hâkim olması gerektiğinde arka planları önerir. background(alignment:content:) başlıklı belgeye bakın.
Bu, alternatif bir eksiksiz programdır. İkinci bir uygulama bildirimi eklemek yerine önceki örneği bununla değiştirin.
import SwiftUI
import Foundation
private struct BadgeWidthKey: PreferenceKey {
static var defaultValue: CGFloat { 0 }
static func reduce(
value: inout CGFloat,
nextValue: () -> CGFloat
) {
value = nextValue()
}
}
@main
struct LegacyWidthDemoApp: App {
var body: some Scene {
WindowGroup {
LegacyWidthDemoView()
}
}
}
struct LegacyWidthDemoView: View {
@State private var measuredWidth: CGFloat = 0
@State private var showLongLabel = false
var body: some View {
VStack(alignment: .leading, spacing: 16) {
Text(showLongLabel ? "Blue circles collection" : "Blue circles")
.font(.system(size: 16))
.lineLimit(1)
.padding(.horizontal, 12)
.padding(.vertical, 8)
.background(Color.blue.opacity(0.12))
.background {
GeometryReader { proxy in
Color.clear.preference(
key: BadgeWidthKey.self,
value: proxy.size.width
)
}
}
.onPreferenceChange(BadgeWidthKey.self) { newWidth in
measuredWidth = newWidth
}
Text(String(
format: "Measured badge: %.1f pt",
Double(measuredWidth)
))
.frame(width: 340, alignment: .leading)
Button("Change sample label") {
showLongLabel.toggle()
}
}
.frame(width: 360, alignment: .leading)
.padding(20)
}
}
PreferenceKey bir değeri üst görünümlere taşır ve onPreferenceChange bu değerdeki değişiklikleri alır. Okuyucu, içerik kapatma ifadesinin içinde durum atamak yerine ölçümü iletir. Apple’ın PreferenceKey ve onPreferenceChange(_:perform:) belgelerine bakın.
Bu birleştirici, kasıtlı olarak tek bir değer gönderen rozet olduğunu varsayar. Aynı anahtarı birden fazla alt görünümde yeniden kullanmak değerleri birleştirir; bu uygulama son birleştirilen değeri tutar. Birbirinden bağımsız etiketleri izlemek için tek bir skaler değeri birden fazla görünümü temsil etmek üzere kullanmak yerine anahtarlı bir ölçüm koleksiyonu tasarlayın.
Ölçümün bağımsız kaldığını nasıl doğrulayabilirsiniz?
İlk programı küçük bir tanılama dizisi olarak kullanın:
- Geniş bir alan önerisiyle
Blue circlesifadesiyle başlayın. Renkli rozeti bilgi alanıyla karşılaştırın. - Kaydırıcıyı sürükleyin. Dış çerçeve bağımsız olarak değişir; yeterli alan kaldığı sürece rozet kompakt kalabilir.
Change sample labelseçeneğini belirleyin, ardından önerilen genişliği azaltın. Dizgenin tamamının ideal genişliğini beklemek yerine mevcut kısıtlanmış rozeti inceleyin.- Gözlemciyi açık çerçevenin altına taşıyın. Artık çerçevenin genişliğini bildirmelidir.
- Rozet ölçümünü başka yerde kullanmadan önce gözlemciyi çerçevenin üstüne geri alın.
Gözlemlenen genişliği aynı görünümün genişliğini belirleyen formülde kullanmayın. Örneğin, çerçevesini gözlemlenen genişliğe dolgu ekleyerek ayarlamak, daha büyük bir sonucu tekrar tekrar düzene geri besleyebilir. Bunun nedeni oluşturduğunuz bağımlılıktır: gözlem durumu değiştirir, durum boyutu değiştirir ve yeni boyut da başka bir gözleme yol açar.
Gerçek amacınız tam bir etiketle kompakt bir seçenek arasında karar vermekse, macOS 13 ve sonrasında ViewThatFits kısıtlı eksenlere sığan ideal boyuta sahip ilk alt görünümü seçebilir. Böylece genişliği saklama gereği ortadan kalkabilir. ViewThatFits başlıklı belgeye bakın.
Birden fazla alt görünümün birlikte ölçülmesi ve konumlandırılması gerekiyorsa macOS 13 ve sonrasında özel bir Layout kullanın. Ölçüm ve yerleştirme yöntemleri, kapsayıcının alt görünümleri konumlandırmadan önce sorgulamasını sağlar; böylece bu karar düzen sürecinin içinde kalır. Layout başlıklı belgeye bakın.
İdeal boyutlandırma veya AppKit metrikleri aynı soruyu yanıtlayabilir mi?
.fixedSize(horizontal: true, vertical: false) özelliğini yalnızca metnin ideal genişliğini korumak istediğinizde kullanın. Apple, bunun metnin üst görünümün sınırlarını aşmasına yol açabileceğini belirtir. Ölçüm elde etmek için eklemek, görüntülenen görünümün boyutlandırma davranışını değiştirir; dolayısıyla özgün kısıtlanmış düzeni korumaz. fixedSize(horizontal:vertical:) başlıklı belgeye bakın.
Karakter sayısı da genişlik ölçümü için uygun değildir. Swift’te String.count, genişletilmiş grafem kümeleriyle temsil edilen Character değerlerini sayar; tipografik boyutları döndürmez. Bu sayıyı tahminî bir karakter genişliğiyle çarpmak SwiftUI görünümünün ölçümünü vermez. Dizeler ve Karakterler başlıklı yazıya bakın.
AppKit’te çizim yaparken NSAttributedString.size(), söz konusu zengin metni çizmek için gereken boyutu döndürür; bu boyut kesirli değerler içerebilir. Apple, bir görünümün boyutunu ayarlarken bu sonucu yukarı yuvarlamayı önerir. Bu, metin çizimiyle ilgili bir soruyu yanıtlar; SwiftUI görünümünün dolgusunu veya onu çevreleyen çerçeveyi içermez. size() başlıklı belgeye bakın.
Mevcut bir TextKit 1 düzeninde NSLayoutManager.usedRect(for:), metin kapsayıcısında o anda düzenlenmiş gliflerin kapladığı alanı bildirir. Bu yöntem tek başına glif oluşturmaz veya düzen gerçekleştirmez; önce düzenin tamamlanması gerekiyorsa ensureLayout(for:) çağırın. Bu API’ler yapılandırılmış metin kapsayıcısını ölçer; sonuçlarını SwiftUI geometrisinden ayrı tutun. usedRect(for:) ve ensureLayout(for:) başlıklı belgelere bakın.
SwiftUI Mac arayüzünüzde zaten görüntülenen etiketi ölçmek için geometrisini ihtiyacınız olan sınırda gözlemleyin, bilgi alanını bağımsız tutun ve hem içerik hem de kullanılabilir alandaki değişiklikleri sınayın. Böylece incelemek istediğiniz düzeni korurken kullanışlı bir genişlik elde edersiniz.
Kaynaklar
onGeometryChange(for:of:action:) — Apple
Basit bir görünümü düzenleme — Apple
GeometryReader — Apple
Görünümünüze arka plan ekleme — Apple
size — Apple
background(alignment:content:) — Apple
PreferenceKey — Apple
onPreferenceChange(_:perform:) — Apple
ViewThatFits — Apple
Layout — Apple
fixedSize(horizontal:vertical:) — Apple
Dizeler ve Karakterler — Swift.org
size() — Apple
usedRect(for:) — Apple
ensureLayout(for:) — Apple