(Input) -> Content olarak, Content türünü View ile kısıtlayarak çağıran tarafın sağladığı içeriği bildirin. Böylece bir macOS bileşeni türü belirlenmiş bir değer sağlayabilirken çağıranlar bu değerin nasıl sunulacağını seçebilir. Sözleşme, bu girdinin kabul edilmesini gerektirir; kullanılmasını gerektiremez.
Bu eğitimde küçük bir masaüstü renk örneği önizlemesi oluşturuluyor. Yeniden kullanılabilir bileşen bir boyut denetimine sahip ve sentetik bir yapılandırmayı çağıranın çizim closure'ına sağlıyor. Çağıran, bileşenin iç durumunu yönetmeden renk örneğini çiziyor.
Bu gereksinimi hangi arayüz ifade eder?
Döndürülen görünüm için adlandırılmış bir genel tür parametresi, bileşenin girdisi içinse somut bir tür kullanın. Aşağıdaki örnekte temel imza, Content: View kısıtlamasıyla tanımlanan bildirimin içinde yer alan @ViewBuilder content: @escaping (SwatchInput) -> Content ifadesidir.
Girdi ve çıktının görevleri ayrıdır. SwatchInput, bileşenin sağlayacağı değerleri tanımlar. Content, her çağıranın closure'ının ürettiği somut görünüm türünü temsil eder. Swift'in Generics bölümünde açıklandığı gibi, adlandırılmış genel tür parametreleri bir bildirimin bu tür ilişkilerini ifade edip yeniden kullanmasını sağlar.
Apple, bu arayüz örüntüsünü GeometryReader içinde kullanır: başlatıcısı (GeometryProxy) -> Content türünde bir closure kabul eder. Kapsayıcı proxy'yi sağlar, çağıran da bundan içerik oluşturur. Bu, kendi yapılandırmasını sağlayan özel bir bileşen için yararlı bir örnektir. Apple'ın GeometryReader ve init(content:) belgelerine bakın.
Gerçek göreve uygun sözleşmeyi seçin:
| Gereksinim | Uygun arayüz |
|---|---|
| Çağıran, bileşen girdisi olmadan içerik sağlar | () -> Content |
| Bileşen belirli bir yapılandırma sağlar | (SwatchInput) -> Content |
| Kapsayıcı, türü kullanımlar arasında değişen bir değer alır | Adlandırılmış bir Input genel tür parametresi ve buna karşılık gelen bir (Input) -> Content closure'ı |
| Derleyicinin çağıranın yapılandırmayı kullandığını kanıtlaması gerekir | Bir closure imzası bu gereksinimi ifade etmez |
Bu seçimler Swift'in işlev türlerinden kaynaklanır: parametre türleri ve dönüş türü, nelerin aktarılıp döndürülebileceğini açıklar. Uygulamanın argümanlarını nasıl kullanması gerektiğini açıklamaz. Swift dil başvurusundaki Function Type bölümüne bakın.
Eksiksiz bir macOS renk örneği önizlemesi oluşturma
SwiftUI kullanan bir macOS uygulama hedefi seçin. Örnekte yerleşik @ViewBuilder yazımı kullanılır ve macOS 12 veya üzeri dağıtım hedefiyle Swift 5.7 ya da üzeri sürümler amaçlanır. Önizleme makroları ve daha yeni içerik oluşturucu adları kullanılmaz. Araştırma ortamında Swift ve Apple SDK'sı bulunmadığından program incelendi ancak burada derlenip çalıştırılmadı.
Bir Xcode SwiftUI uygulama projesinde, oluşturulan uygulama bildirimini aşağıdaki eksiksiz programla değiştirin. Oluşturulan ContentView bildirimi bununla çakışacaksa kaldırın. @main ile işaretlenmiş yalnızca bir uygulama giriş noktası bırakın. Apple'ın Xcode yardım belgeleri, SwiftUI projesiyle başlamayı veya mevcut bir projeye SwiftUI dosyası eklemeyi açıklar.
import SwiftUI
struct SwatchInput {
let title: String
let side: CGFloat
let color: Color
}
@MainActor
struct SwatchPanel<Content: View>: View {
@State private var isLarge = false
private let content: (SwatchInput) -> Content
init(
@ViewBuilder content: @escaping (SwatchInput) -> Content
) {
self.content = content
}
private var input: SwatchInput {
SwatchInput(
title: "Sample A",
side: isLarge ? 120 : 64,
color: .blue
)
}
var body: some View {
VStack(alignment: .leading, spacing: 16) {
Toggle("Large sample", isOn: $isLarge)
content(input)
}
.padding(24)
}
}
@MainActor
struct ContentView: View {
var body: some View {
SwatchPanel { input in
Text(input.title)
.font(.headline)
RoundedRectangle(cornerRadius: 12)
.fill(input.color)
.frame(width: input.side, height: input.side)
if input.side == 120 {
Text("Large preview")
.font(.caption)
}
}
.frame(width: 300, height: 280, alignment: .topLeading)
}
}
@main
@MainActor
struct TypedContentDemoApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
İlk ekranda Large sample anahtarının, Sample A başlığının ve kenar uzunluğu 64 nokta olan mavi, yuvarlatılmış bir karenin görünmesi beklenir. Anahtarı açmak, sağlanan kenar uzunluğunu 120 noktaya çıkarır ve Large preview yazısını ekler. Bu boyutlar ve etiketler ölçüm sonuçları değil, örnek amaçlı yerel değerlerdir.
Bileşen isLarge öğesine sahiptir. Hesaplanan input geçerli seçimi paketler ve content(input) bu paketi çağırana aktarır. State belgesinde açıklandığı üzere SwiftUI, durum depolamasını yönetir ve değişen bir değere bağlı görünüm hiyerarşisi bölümlerini günceller.
Sunumu çağıran belirler: bir başlık, bir şekil ve isteğe bağlı bir açıklama. Aynı türü belirlenmiş girdiyi kullanırken başka bir düzen seçebilir. Hem panel hem de onu çağıran, arayüzlerini oluşturmak için Apple'ın belgelerinde açıklanan View modelini izleyerek body uygular.
Oluşturucu ve escaping açıklamaları neden gerekli?
@ViewBuilder, çağıranın tek bir closure içinde birkaç görünüm ifadesi ve desteklenen koşullu dallar yazmasını sağlar. Bu örnekte çağıran, koşulsuz iki görünüm ve koşullu bir açıklama sağlar. Oluşturucu, bu içeriği bileşenin dönüş kısıtlamasını karşılayan bir sonuca dönüştürür. Apple, birden fazla alt görünümü ve koşullu oluşturmayı ViewBuilder altında belgeler.
Oluşturucu SwatchInput sağlamaz. Normal closure parametresi girdiyi bildirir, content(input) ise gerçek argümanı sağlar. Başlatıcı () -> Content kabul etseydi, oluşturucu açıklaması eklemek onu yapılandırma kabul eden bir closure'a dönüştürmezdi.
Başlatıcı closure'ı daha sonra body içinden çağırmak üzere bir özellikte sakladığından @escaping gerekir. Swift'in Closures bölümü, alıcı işlev döndükten sonra çağrılabilen closure'ın escaping olduğunu açıklar.
Yapılandırması başlatma sırasında kesinleşmiş bir bileşen için başka bir tasarım, escaping olmayan bir oluşturucuyu hemen çağırıp ortaya çıkan Content değerini saklamaktır. Bu, yapılandırmanın uygulanma zamanını değiştirir. Buradaysa closure'ı saklamak amaçlanan akışı açıkça gösterir: bileşen durumundan geçerli girdiyi türet, ardından bu girdiyi kullanarak içeriği oluştur.
Xcode oluşturucu sınırını koruma
Apple'ın güncel belgeleri Xcode 26 ve önceki sürümleri, Xcode 27 ve sonraki sürümlerden ayırır. Önceki araçlar için ViewBuilder'ı görünümler oluşturan bir yapı olarak açıklar. Xcode 27 ve sonraki sürümlerde türden bağımsız oluşturmayı açıklar ve bu daha geniş davranışı belirtmek için ContentBuilder takma adını önerir. Bkz. ViewBuilder.
Xcode 27 veya üzerini kullanan bir kod tabanını sürdürürken başlatıcıda bunun yerine @ContentBuilder kullanılabilir. Apple bunu, uyumluluğun ürettiği içerik türleri aracılığıyla uygulandığı bir ViewBuilder tür takma adı olarak belgeler. Çıktısı hâlâ bir görünüm hiyerarşisi için tasarlandığından bu bileşende Content: View kısıtlamasını koruyun. Güncel belgelerde eski dağıtım hedefleri listeleniyor diye daha yeni yazımın eski SDK'larda bulunduğunu varsaymayın. Bkz. ContentBuilder.
some View bu sözleşmenin her bölümünü neden çözmez?
Örnekteki body özellikleri için some View uygundur. Somut sonuç türünü gizlerken sonucun bir görünüm olduğu güvencesini korur. Saklanan closure ise adlandırılmış Content parametresini kullanır; böylece bileşen aynı sonuç türüne hem özelliğinde hem başlatıcısında başvurabilir.
Swift 5.7, SE-0341 aracılığıyla opak parametre bildirimlerini kullanıma sundu. _ value: some View gibi doğrudan bir parametre, somut türü çağıranın belirlediği adlandırılmamış bir genel tür parametresi oluşturur. Bu, işlev uygulamasının keyfî bir görünüm seçip onu bu adlandırılmamış türün beklendiği her yere aktarabileceği anlamına gelmez. Bkz. Opaque Parameter Declarations.
Özellikle, (some View) -> Content biçiminde yazılmış bir geri çağırım parametresi, SE-0341'de belgelenen desteklenen opak parametre konumlarının dışındadır. Öneri, geri çağırımın işlev türündeki girdi konumunda opak türleri açıkça yasaklar. Bu sözdiziminin ileride etkinleştirilebilmesine ilişkin tartışma, uygulanmış davranışlar arasında değil, geleceğe yönelik değerlendirmeler bölümündedir. Güncel Types başvurusu bu kısıtlamayı korur.
Bu masaüstü görevi için desteklenen yol basittir: bileşenin sağladığı değeri somut bir yapılandırma türü yapın. Farklı bir yeniden kullanılabilir kapsayıcının çağıranın sağladığı girdi türünü koruması gerekiyorsa, bu türü bir genel tür parametresiyle adlandırın ve saklanan değer ile geri çağırım boyunca tutarlı biçimde kullanın. Her iki tasarım da opak bir geri çağırım girdisi gerektirmez.
Derleyici gerçekte neyi garanti eder?
İmza, çağrılabilir bir biçimi garanti eder: bir SwatchInput argümanı ve Content: View kısıtlamasını karşılayan bir sonuç. Ayrıca closure parametresine çıkarımla türünü verir; böylece çağıran, tür açıklamasını tekrarlamadan input.title, input.side ve input.color öğelerine erişebilir. Swift, bu çıkarımı closure bağlamından Closures bölümünde açıklar.
Bu özelliklerin ekranda etkili olacağını garanti edemez. Örneğin, ContentView.body içindeki geçerli bir alternatif ifade aşağıdadır; yapılandırmayı bilerek kabul eder ve yok sayar:
SwatchPanel { _ in
Text("Fixed preview")
}
Bileşen gereksinimleri yazılırken bu sınır önemlidir. “Çağıran içeriği SwatchInput kabul eder” bir tür sözleşmesidir. “Çağıran içeriği sağlanan boyutu görüntüler” ise davranışsal bir gereksinimdir. Bu boyutın görüntülenmesi bileşen için vazgeçilmezse, bu çizimi bileşenin içine alın ve özelleştirilebilir alanı küçültün.
Entegrasyon sorunlarını tanılamak için aşağıdaki kontrolleri kullanın. Bunlar, arayüzden çıkarılan inceleme adımlarıdır; derleyici testi sonucu olarak raporlanmamıştır:
| Belirti | Kontrol |
|---|---|
content çağrılırken eksik argüman hatası |
Yapılandırmayı aktarın: content(input). |
| Başlatıcıda escaping closure tanısı | Geri çağırımı saklarken @escaping açıklamasını koruyun. |
| Çağırandaki birkaç kardeş görünüm hataya neden oluyor | Başlatıcı parametresinde uygun oluşturucu açıklamasını koruyun. |
| Açıkça türü belirtilmiş bir geri çağırım reddediliyor | Girdisinin String gibi ilgisiz bir tür yerine SwatchInput olduğunu kontrol edin. |
| Önizleme boyut anahtarını yok sayıyor | Çizimin input.side öğesini okuyup okumadığını kontrol edin. |
| Bileşen başlangıç yapılandırmasını göstermeye devam ediyor | İçeriğin bir kez oluşturulup saklanmak yerine geri çağırımın geçerli girdiyle çağrılıp çağrılmadığını kontrol edin. |
Sözleşmeyi görevin izin verdiği ölçüde küçük tutun. Somut bir yapılandırma ve genel bir görünüm sonucu, bu renk örneği paneline açık bir sınır verir: bileşen güncel değerleri sağlar, çağıran bunları tüm tür bilgisiyle alır ve seçilen sunum kolayca değiştirilebilir.
Kaynaklar
- Generics — Swift
- Types — Swift
- Closures — Swift
- Opaque Parameter Declarations — Swift Evolution
- GeometryReader — Apple
- init(content:) — Apple
- ViewBuilder — Apple
- ContentBuilder — Apple
- View — Apple
- State — Apple
- Show a preview of your user interface — Apple