Mac uygulamasında yeniden kullanılabilir bir SwiftUI paneli için ana içerik builder’ını ilk sıraya koyun, diğer builder’ları etiketleyin ve atlanan bölgeler için kısıtlı başlatıcılar kullanın. Böylece çağrılar okunabilir kalır ve bulunmayan üstbilgi ile altbilgilere açık türler atanır.
Bu eğitimde içerik, üstbilgi ve altbilgi bölgeleri olan küçük bir bileşen oluşturuluyor. Amaç, varsayılan değerli closure’ların birbiriyle yarışmasına bel bağlamadan dört kullanışlı çağrıyı desteklemek: yalnızca içerik, içerikle birlikte iki ek bölgeden biri ve içerikle birlikte her iki ek bölge. Gösterilen tüm değerler sentetik yerel metinlerdir.
Birden fazla builder kullanan bir başlatıcıyı belirsiz olmayan hâle getiren nedir?
Swift’in birden fazla sondaki closure söz dizimi, ilk sondaki closure’ın bağımsız değişken etiketini kaldırır ve sonraki sondaki closure’lar için etiket ister. Bu nedenle bir bileşen, ilk builder’ın amacını bileşenin adından anlaşılır kılmalı ve sonraki builder’lara farklı etiketler vermelidir. Bu söz dizimi Swift 5.3’te uygulandı. SE-0279: Birden Fazla Sondaki Closure
Bu panel için tam başlatıcı init(content:header:footer:) şeklindedir. Çağrı noktasında ilk blok içeriği sağlar; header: ve footer: diğer blokları belirtir. Bildirim sırası bağımsız değişkenlerin sırasını, bileşenin body değeri ise görsel sırasını belirler. Bu nedenle üstbilgi içeriğin üstünde görünse de içerik ilk bağımsız değişken olabilir.
Apple’ın güncel Section arayüzü, içerik-öncelikli bu düzeni izler. Tam başlatıcısı üç builder closure’ı kabul eder; bulunmayan bölgelerin çeşitli birleşimleri için ayrı başlatıcılar vardır. Bu, arayüz tasarımı için yararlı bir örnektir; aşağıdaki özel panelin kendine özgü bir düzeni ve davranışı vardır. Apple: Section, Apple: init(content:header:footer:)
Bir builder niteliği ile bağımsız değişken etiketi farklı sorunları çözer. Sonuç builder niteliği, sağlanan closure’ın gövdesini dönüştürür; bağımsız değişken etiketleri ise çağıranların parametreleri nasıl tanımlayacağını belirler. @ViewBuilder eklemek, sondaki closure söz dizimi ilk etiketi kaldırdığında onu geri getirmez. SE-0289: Sonuç builder’ları
Bu ayrım pratik bir tasarım kuralı getirir: tek yararlı farkı, ilk sondaki closure’ın etiketi kaldırılınca kaybolan kardeş başlatıcılardan kaçının. Örneğin, diğer yönlerden eşdeğer olan init(content:header:) ve init(body:header:) adlı iki başlatıcı, etiketsiz aynı ilk bloğu ve ardından header: etiketini sunar. Bu, daha fazla builder niteliği eklemek için bir gerekçe değil, sondaki closure kurallarının tasarıma yansıyan bir sonucudur.
Hangi derleyici ve SwiftUI sınırları önemlidir?
Üç ayrıntıyı göz önünde bulundurun: derleyici, seçilen Swift dil modu ve SwiftUI SDK’sı. Birden fazla sondaki closure Swift 5.3’te, resmî sonuç builder özelliği ise Swift 5.4’te kullanıma sunuldu. Bu kilometre taşları dil desteğini açıklar; her framework başlatıcısının kullanılabilir olduğunu göstermez. SE-0279, SE-0289
Apple’ın güncel belgeleri de araç zinciri sınırını açıklıyor: Xcode 26 ve önceki sürümler yerleşik görünüm oluşturma davranışını kullanırken, Xcode 27 ve sonraki sürümler bu builder’lardan türden bağımsız içerik oluşturuyor. Apple, yeni davranış için geliştiricileri @ContentBuilder yazımına yönlendiriyor. ContentBuilder, ViewBuilder için bir tür takma adıdır; kabul edilebilir içeriği yine kapsayan API’nin kısıtlamaları belirler. Apple: ViewBuilder, Apple: ContentBuilder
Aşağıdaki eksiksiz örnek, Xcode 26 tabanı için @ViewBuilder yazımını kullanır ve macOS 11 veya sonraki sürümleri hedefler. Belgelenen Xcode 27 arayüz biçimini benimserken başlatıcı parametrelerindeki nitelikleri @ContentBuilder olarak değiştirin. Üç genel View kısıtlamasını koruyun: bu bileşen, üç bölgenin tamamını bir görünüm düzeninin içine yerleştirir.
Bileşen sözleşmesinde builder’ın ürettiği tuple türünü adlandırmaktan kaçının. Apple, yeni builder uygulamasına geçişte açık TupleView kısıtlamalarını içeren kaynak uyumsuzluklarını belgeliyor. Genel bölge türleri, bu panelin söz konusu bağımlılıktan kaçınmasını sağlar. Apple: TN3211, State ve ContentBuilder için SwiftUI kaynak uyumsuzluklarını giderme
Dört açık başlatıcı biçimiyle bir panel oluşturma
Her bölge için bağımsız bir genel tür kullanın. Tek bir genel parametreyi yeniden kullanmak, bölgelerin aynı somut türe sahip olmasını gerektirir; oysa biri tek bir metin görünümü, diğeri birkaç görünüm içeriyorsa bu gereksiz bir kısıtlamadır.
Bulunmayan bir bölge için genel parametresini EmptyView türüne özelleştirin. Apple, EmptyView için tam da bu rolü belgeliyor: genel bir görünüm türünde bulunmayan bir alt öğeyi temsil etmek. Aşağıdaki kısıtlı başlatıcılar, çağıranlardan belirtilmemiş bir ek bölge türünü çıkarsamalarını istemek yerine bu seçimi açıkça yapar. Apple: EmptyView
Bu bölümdeki iki Swift bloğu, tek bir eksiksiz uygulama dosyası oluşturur. macOS SwiftUI uygulama hedefinde, oluşturulan uygulama ve içerik görünümü bildirimlerini bu bloklarla değiştirin ve tam olarak bir @main bildirimi bırakın. İlk blok bileşeni ve dört başlatıcı biçiminin tamamını tanımlar.
import SwiftUI
struct BuilderPanel<Content: View, Header: View, Footer: View>: View {
private let content: Content
private let header: Header
private let footer: Footer
init(
@ViewBuilder content: () -> Content,
@ViewBuilder header: () -> Header,
@ViewBuilder footer: () -> Footer
) {
self.content = content()
self.header = header()
self.footer = footer()
}
var body: some View {
VStack(alignment: .leading, spacing: 8) {
header
content
footer
}
.padding()
}
}
extension BuilderPanel where Footer == EmptyView {
init(
@ViewBuilder content: () -> Content,
@ViewBuilder header: () -> Header
) {
self.init(
content: content,
header: header,
footer: { EmptyView() }
)
}
}
extension BuilderPanel where Header == EmptyView {
init(
@ViewBuilder content: () -> Content,
@ViewBuilder footer: () -> Footer
) {
self.init(
content: content,
header: { EmptyView() },
footer: footer
)
}
}
extension BuilderPanel where Header == EmptyView, Footer == EmptyView {
init(@ViewBuilder content: () -> Content) {
self.init(
content: content,
header: { EmptyView() },
footer: { EmptyView() }
)
}
}
Tam başlatıcı, bu panel değerinin başlatılması sırasında her builder’ı bir kez çağırır ve dönen değerleri saklar. Kolaylık başlatıcıları tam başlatıcıyı çağırır. Bu zamanlama, bu uygulamanın bir özelliğidir: arayüzün ömrü boyunca bir builder’ın yalnızca bir kez çalışacağını vaat etmez; çünkü başka bir panel değeri oluşturulduğunda başlatıcısı yeniden çağrılır.
İkinci bloğu aynı dosyanın sonuna ekleyin. Bu blok, tek bir builder’da birden fazla ifade bulunması da dâhil olmak üzere desteklenen her biçimi kullanır.
struct PanelDemo: View {
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 16) {
BuilderPanel {
Text("Canvas A")
}
BuilderPanel {
Text("Canvas B")
Text("Grid: 12 columns")
} header: {
Text("Layout")
}
BuilderPanel {
Text("Canvas C")
} footer: {
Text("Units: points")
}
BuilderPanel {
Text("Canvas D")
} header: {
Text("Preview")
} footer: {
Text("Scale: 100 percent")
}
}
.padding()
}
.frame(minWidth: 360, minHeight: 440)
}
}
@main
struct BuilderPanelDemoApp: App {
var body: some Scene {
WindowGroup {
PanelDemo()
}
}
}
Seçilen SDK’ya göre sözleşmeyi doğrulamak için uygulamayı Xcode’da derleyip çalıştırın. Kod burada incelendi ancak derlenmedi veya çalıştırılmadı; araştırma ortamında Swift derleyicisi ya da macOS SwiftUI SDK’sı yoktu.
Her çağrıyı somut bir tür sözleşmesiyle eşleştirin
Yararlı karar aracı, çağrı biçimlerini gösteren bir tablodur. Hangi başlatıcının seçilmesi gerektiğini ve eksik bölgelere hangi somut türlerin atandığını belirtir. Bunlar, örneğin bildirimlerinden çıkarılan ve projenizde kontrol edilmeye hazır beklentilerdir.
| Sağlanan bölgeler | Başlatıcı etiketleri | Bulunmayan bölge kısıtlamaları |
|---|---|---|
| Yalnızca içerik | content: |
Header == EmptyView, Footer == EmptyView |
| İçerik ve üstbilgi | content:header: |
Footer == EmptyView |
| İçerik ve altbilgi | content:footer: |
Header == EmptyView |
| Üçü de | content:header:footer: |
Yok |
Daha kısa başlatıcıların her biri, bildirdiği tüm closure’ları zorunlu kılar. Hiçbir varsayılan bağımsız değişken, üç builder’lı başlatıcının yalnızca içerik çağrısıyla yarışmasına yol açmaz. Yalnızca üstbilgi ve yalnızca altbilgi ekleme biçimleri, her iki closure da Text döndürse bile ikinci etiketleri sayesinde birbirinden ayrılır.
Bileşeni değiştirirken bu tabloyu kullanın: Yeni bir başlatıcı mevcut bir çağrı biçimini kabul ediyorsa, ilk sondaki etiket kaldırıldıktan sonra yeni ayrımın hâlâ görünür olup olmadığını kontrol edin. Böylece arayüz çakışmasını, daha büyük bir görünümde anlaşılması zor bir tanılama mesajına dönüşmeden önce yakalayabilirsiniz.
Birkaç varsayılan değerli builder closure’ından neden kaçınmalı?
Varsayılan bağımsız değişkenler yararlıdır, ancak varsayılan değerli birkaç closure parametresi Swift dil moduna bağlı eşleştirme kuralları getirir. SE-0286, varsayılan değerli parametreleri atlamaya yönelik bir geriye uyumluluk sezgiseli olan ileriye doğru eşleştirmeyi ve eski modlardaki geriye doğru eşleştirme yedeğini belgeliyor. Ayrıca Swift 5.8’de uygulanan ForwardTrailingClosures gelecek özellik bayrağını da belgeliyor. SE-0286: Sondaki closure’lar için ileriye taramalı eşleştirme
Buradaki pratik öneri, bu kurallardan bilinçli olarak daha dardır: Küçük ve yeniden kullanılabilir bir panel bunlara bel bağlamadan tasarlanabilir. Ana builder’ını zorunlu tutun ve desteklenen eksiltmeleri kısıtlı başlatıcılarla ifade edin. Böylece desteklenen birleşimler bildirimlerde görünür olur ve derleyici sürümüne duyarlı eşleştirme davranışının bileşenin amaçlanan anlamının bir parçası olarak kullanılmasına gerek kalmaz.
Atlanan bir ek bölgeyle boş bir ek bölge closure’ı da arayüz açısından farklı seçimlerdir. Yalnızca içerik alan başlatıcı, her iki ek bölge için açıkça EmptyView seçer. Boş bir builder sağlamak, closure’ın sonucunu SDK’nın builder uygulamasının belirlemesini ister. Bu bileşende kullanılmayan bölgeleri atlayın ve kolaylık başlatıcısının belgelenmiş yokluk türünü sağlamasına izin verin.
Bu yaklaşımın ölçek sınırı vardır. İçeriğin her zaman gerekli olduğu durumda üç bölge, dört yararlı birleşim oluşturur. Birbirinden bağımsız olarak isteğe bağlı çok sayıda bölgesi olan bir bileşen için çok daha büyük bir aşırı yük ailesi gerekir. Bu aşırı yükleri eklemeden önce, okuyucuların gerçekten hangi birleşimlere ihtiyaç duyduğuna karar verin; daha küçük ve anlamlı bir bileşen varyantı kümesinin kullanımı ve bakımı daha kolay olabilir.
Belirsiz bir çağrının tanısı nasıl konur?
Sorunu yeniden üreten en küçük panel çağrısıyla başlayın. Ardından şu adımları izleyin; böylece bağımsız değişken eşleştirmesindeki sorunları builder içindeki hatalardan ayırabilirsiniz.
- Tüm closure bağımsız değişkenlerini parantez içine yazın.
content:,header:vefooter:etiketlerini bildirim sırasıyla açıkça belirtin. Bu çağrıyı çözerse, sondaki closure söz diziminin hangi ayrımı kaldırdığını inceleyin. - Her bölgeyi geçici olarak tek bir
Textifadesiyle değiştirin. Çağrı bu durumda çalışırsa, sorunlu builder gövdesini bulmak için özgün ifadeleri teker teker geri ekleyin. - Başlatıcı biçimlerini karşılaştırın. Birbiriyle yarışan varsayılanları, ters çevrilmiş closure sıralarını ve yalnızca ilk builder etiketiyle ayrılan aşırı yükleri arayın.
- Bulunmayan bölge kısıtlamalarını kontrol edin. Atlanan her genel bölgenin, seçilen başlatıcıda amaçlanan somut bir türü olmalıdır.
- Araç zinciri sınırını kontrol edin. Xcode 27’ye geçtikten sonra başlatıcı ailesini yeniden tasarlamadan önce açık builder sonucu kısıtlamalarını ve çakışan tür adlarını inceleyin.
Etiketleri parantez içine yazmak, ilk sondaki closure’ın atladığı bağımsız değişken adlarını korur. Swift her iki biçimi de destekler; bu nedenle tüm etiketlerin açıkça yazıldığı biçim, hem kullanışlı bir tanılama aracıdır hem de belirli bir çağrının okunmasını kolaylaştırıyorsa uygun bir son yazımdır. The Swift Programming Language: Closure’lar
Apple, yeni SDK tanılama mesajları için farklı modüllerde çakışan türlerin nitelemeyle belirtilmesini ve eski tuple çıktı türlerine bağlı kısıtlamaların güncellenmesini özellikle öneriyor. Bu sorunlar, başka yönlerden iyi tasarlanmış bir başlatıcı çağrısında da ortaya çıkabilir; daha fazla aşırı yük eklemek altta yatan sorunu çözmez. Apple: TN3211
Açık bir başlatıcı sözleşmesi, bileşenin desteklemesi gereken çağrılarla başlar. Birincil içeriğini ilk sıraya koyun, ek bölge builder’larına anlamlı etiketler verin ve bulunmayan bölgelere açık türler atayın. Ardından arayüzü genişletmeden önce desteklenen her çağrı biçimini seçtiğiniz derleyici, dil modu ve SDK ile derleyin.
Kaynaklar
- Birden Fazla Sondaki Closure — SE-0279 — Swift Projesi.
- Sondaki closure’lar için ileriye taramalı eşleştirme — SE-0286 — Swift Projesi.
- Sonuç builder’ları — SE-0289 — Swift Projesi.
- Closure’lar — The Swift Programming Language — Swift Projesi.
- Section — Apple.
- init(content:header:footer:) — Apple.
- ViewBuilder — Apple.
- ContentBuilder — Apple.
- EmptyView — Apple.
- TN3211: State ve ContentBuilder için SwiftUI kaynak uyumsuzluklarını giderme — Apple.