Paylaşılan gözlemlenebilir durumu SwiftUI App’inizde tutun, ardından aynı örneği her iki sahneye de aktarın. Her pencerenin taslağını kök görünümünde tutun. Böylece menü çubuğu denetimi her pencereyi güncelleyebilirken her pencere kendi tamamlanmamış metnini korur.
Bu eğitim, küçük bir Mac etiket düzenleyicisi için bu yapıyı oluşturuyor: birden çok pencere aynı etiketi gösteriyor, her pencerenin bağımsız bir taslağı var ve menü çubuğu paneli paylaşılan etiketi düzenleyebiliyor veya başka bir pencere açabiliyor. Tüm örnek metinler uygulamaya aittir ve sentetik etiketler kullanır.
Hangi değişikliklerin her pencereye ulaşması gerektiğine karar verin
Her değerin nasıl davranması gerektiğinden başlayın. Düzenlendiğinde paylaşılan etiket her yerde değişmelidir. Tamamlanmamış bir taslak yalnızca yazı yazılan pencerede değişmelidir. Değerlerin nerede tutulacağını bu gereksinimler belirler.
Apple, WindowGroup tarafından oluşturulan her pencerenin, görünüm hiyerarşisi içinde başlatılan durum için bağımsız depolamaya sahip olduğunu belgeliyor. Buna karşılık Apple’ın model verisi yönergeleri, üst düzey App içinde gözlemlenebilir bir referans tutmayı ve bunu bir görünüm hiyerarşisine aktarmayı gösteriyor. Bu kalıpları birleştirmek şu sahiplik planını verir. WindowGroup, Uygulamanızdaki model verilerini yönetme.
| Değer | Bu örnekte sahibi | Amaçlanan davranış |
|---|---|---|
| Paylaşılan etiket | @Observable model kullanan @State ile App |
Her pencere ve menü çubuğu paneli aynı değeri düzenler |
| Tamamlanmamış taslak | @State kullanan her pencerenin kök görünümü |
Yazılanlar o pencereye özel kalır |
| Menü çubuğu denetimleri | Uygulamanın sahibi olduğu modeli alır | Değişiklikler bu örneğin tüm kullanıcılarına ulaşır |
| Pencere açma eylemi | Menü çubuğu görünümünün ortamından okunur | Belirtilen gruptan bir pencere açar |
Ayrım, örneğin sahibidir. İki ayrı oluşturulmuş nesneye aynı sınıf adını vermek, içeriklerini paylaşılır hâle getirmez. Benzer şekilde, uygulamanın sahip olduğu tek bir nesneye yapılan referansı birkaç pencereye koymak da o nesnenin bağımsız kopyalarını oluşturmaz. Bu nedenle örnek, paylaşılan modelini uygulama düzeyinde yalnızca bir kez oluşturur.
Sahneleri eklemeden önce macOS temel sürümünü belirleyin
macOS 14 veya üzerini hedefleyen ve Xcode 15 veya sonrası gibi Observation’ı destekleyen bir Xcode araç zincirine sahip macOS SwiftUI uygulama projesi kullanın. SwiftUI’nin Observation entegrasyonu macOS 14’te başlar. MenuBarExtra ve pencere stili macOS 13’ten itibaren kullanılabilir; dolayısıyla bu uygulamanın daha yüksek temel sürümünü Observation belirler. Uygulamanızdaki model verilerini yönetme, MenuBarExtra, pencere.
Menü çubuğu paneli metin alanları ve özel bir düzen içerdiği için .menuBarExtraStyle(.window) kullanır. Apple bu stili, denetimleri normal bir penceredekilere benzer biçimde düzenlenmiş, açılır pencere benzeri bir pencere olarak tanımlar. Bu, düzenleyicinin WindowGroup’undan ayrı bir sahnedir. pencere.
Örnek ayrıca modelini ve kullanıcı arayüzü türlerini açıkça @MainActor ile sınırlar. Apple, ana aktörü yürütücüsü ana gönderim kuyruğuna eşdeğer olan aktör olarak tanımlar. Burada tüm değişiklikler kullanıcı arayüzü denetimlerinden kaynaklanır; arka plan işi gerekmez. MainActor.
Paylaşılan modeli ve bağımsız pencere taslaklarını oluşturun
Oluşturulan uygulama giriş noktasını aşağıdaki eksiksiz, tek dosyalık örnekle değiştirin. Hedefte yalnızca bir @main bildirimi bırakın. Örnek; içe aktarımları, modeli, görünümleri ve başlangıç metnini içerir. Belgelenen API’lerle uyumu incelendi ancak bu ortamda Swift araç zinciri bulunmadığından derlenemedi.
import SwiftUI
import Observation
@MainActor
@Observable
final class LabelStore {
var sharedLabel = "Sample Alpha"
}
@main
@MainActor
struct LabelScopeApp: App {
@State private var store = LabelStore()
var body: some Scene {
WindowGroup("Label Editor", id: "label-editor") {
EditorWindow()
.environment(store)
}
MenuBarExtra("Label Controls", systemImage: "square.grid.2x2") {
LabelMenuPanel()
.environment(store)
}
.menuBarExtraStyle(.window)
}
}
@MainActor
struct EditorWindow: View {
@Environment(LabelStore.self) private var store
@State private var draft = "Sample Draft"
var body: some View {
VStack(alignment: .leading, spacing: 16) {
SharedLabelControls()
Divider()
TextField("This window's draft", text: $draft)
.textFieldStyle(.roundedBorder)
Button("Use Draft as Shared Label") {
store.sharedLabel = draft
}
Text("Publishing the draft keeps its local text intact.")
.font(.caption)
}
.padding(20)
.frame(minWidth: 420, minHeight: 220)
}
}
@MainActor
struct SharedLabelControls: View {
@Environment(LabelStore.self) private var store
var body: some View {
@Bindable var editableStore = store
VStack(alignment: .leading, spacing: 8) {
Text("Shared label: \(store.sharedLabel)")
TextField("Shared label", text: $editableStore.sharedLabel)
.textFieldStyle(.roundedBorder)
}
}
}
@MainActor
struct LabelMenuPanel: View {
@Environment(\.openWindow) private var openWindow
var body: some View {
VStack(alignment: .leading, spacing: 16) {
SharedLabelControls()
Button("Open Another Editor") {
openWindow(id: "label-editor")
}
}
.padding(16)
.frame(width: 320)
}
}
Açıkça iki ortam enjeksiyonu vardır: biri pencere içeriğinde, diğeri menü çubuğu içeriğinde. Her ikisi de store alır. Pencere içeriğine eklenen bir ortam değiştiricisinin bağımlılığı kardeş bir sahneye sağlayacağını varsaymayın. Apple, ortamın görünüm hiyerarşisi boyunca paylaşılmasını belgeliyor; bu iki enjeksiyon her hiyerarşinin bağımlılığını açıkça belirtir. Uygulamanızdaki model verilerini yönetme.
Denetimin bağlamaya ihtiyaç duyduğu yerde @Bindable kullanın
SharedLabelControls, modeli @Environment(LabelStore.self) üzerinden okur. Gövdesinde yerel @Bindable değişkeni, TextField için gereken projeksiyon bağlamasını sağlar. Var olan gözlemlenebilir nesneye başvurur ve başka bir depo oluşturmaz. Apple, ortamdan bağlamaya uzanan bu kalıbı aynen belgeliyor. Bindable.
Taslak alanı bunun yerine $draft kullanır; çünkü değeri zaten pencere görünümünün @State’i tarafından tutulur. Düğme geçerli taslağı paylaşılan etikete kopyalar. Bu açık bir eylemdir: paylaşılan etiketteki sonraki değişiklikler hiçbir pencerenin taslağını yeniden yazmaz. Bunlar örneğin seçtiği düzenleme davranışlarıdır.
Pencere grubu tanımlayıcısıyla tam olarak eşleşin
Menü çubuğu düğmesi openWindow değerini kendi ortamından alır ve grup bildirimiyle eşleşen \"label-editor\" bağımsız değişkeniyle çağırır. Apple, bir gruptan pencere oluşturmak için bu tanımlayıcı tabanlı yaklaşımı belgeliyor. Düğmenin amacı başka bir düzenleyici açmaktır; bu nedenle örnek, özellikle mevcut bir pencereyi bulup yeniden kullanmayı vaat etmez. WindowGroup.
İki pencereli bir işlem dizisiyle kapsamı doğrulayın
Aşağıdaki sıra, örneğin beklenen davranışını kontrol eder. Ek ölçümleme gerektirmeden paylaşılan model kimliğini bağımsız taslak depolamadan ayırır.
- Uygulamayı başlatın. İlk düzenleyicide paylaşılan etiket
Sample Alpha, taslak iseSample Draftolarak görünür. - Menü çubuğu panelini açıp
Open Another Editorseçeneğini seçin. - İlk pencerenin taslağını
Draft Oneolarak değiştirin. İkinci pencerenin taslağını değiştirmeyin. - Menü çubuğu panelinde paylaşılan etiketi
Sample Betaolarak değiştirin. Her iki düzenleyici penceresi de farklı taslak değerlerini korurken paylaşılan etiketi göstermelidir. - İlk pencerede
Use Draft as Shared Labelseçeneğini seçin. Paylaşılan etiket her iki pencerede ve menü çubuğu panelindeDraft Oneolur. İkinci pencerenin taslağıSample Draftolarak kalır.
Bu sıra üç ayrı yolu kontrol eder: yerel yazma, menü çubuğundan düzenleme ve yerelden paylaşılan değere açıkça kopyalama. Yerel yazma diğer pencerenin taslağını değiştiriyorsa taslağın uygulama sahipliğindeki depoya taşınıp taşınmadığını inceleyin. Menü çubuğu düzenlemeleri pencerelere ulaşmıyorsa menü çubuğu hiyerarşisinde başka bir LabelStore() oluşturulup oluşturulmadığını inceleyin.
| Belirti | İlk incelenecek şey |
|---|---|
| Pencere ve menü çubuğu etiketleri farklılaşıyor | Tek bir paylaşılan örnek yerine ayrı model başlatmaları |
| Tüm taslaklar birlikte değişiyor | Taslak depolamasının paylaşılan modele konması |
| Bir görünüm modelini okurken hata veriyor | Bu hiyerarşide .environment(store) eksik olması |
| Bir düzenleyici beklenmedik biçimde sıfırlanıyor | Sahip görünümün kimliğinin değişmesi veya modelinin değiştirilmesi |
| Başka bir düzenleyici açılamıyor | Tanımlanan sahne tanımlayıcısıyla istenen sahne tanımlayıcısının uyuşmaması |
Eksik ortam durumunun belgelenmiş bir sonucu vardır: türe göre isteğe bağlı olmayan bir gözlemlenebilir nesnenin okunması, üst öğenin bunu sağladığını varsayar; Apple, nesne yoksa istisna oluşacağını belirtir. Bağımlılığı önizlemelerde ve alternatif giriş noktalarında da sağlayın. Uygulamanızdaki model verilerini yönetme.
Örnek her şeyi bellekte tutar ve kaydetme ya da geri yükleme uygulaması içermez. Bu nedenle bir pencerenin kapatılması tamamlanmamış taslağın atılması, uygulamadan çıkılması ise paylaşılan etiketin atılması olarak değerlendirilmelidir. Taslakların bu eylemlerden sonra da korunması gerekiyorsa örneği genişletmeden önce ayrı bir kalıcılık gereksinimi tanımlayın.
Odaklı komutları paylaşılan menü çubuğu denetimlerinden ayrı tutun
Paylaşılan bir etiket için etkin pencere seçimi gerekmez: her kullanıcı aynı modeli alır. O anda odakta olan pencerenin taslağını düzenleyen bir eylem ise farklı bir gereksinime sahiptir; çünkü belirli bir pencereyi tanımlamalıdır.
Standart uygulama menüsü komutları için Apple, pencereye ait bir modeli focusedSceneValue ile yayımlamayı ve bunu Commands uygulamasında @FocusedValue üzerinden almayı belgeliyor. Örneğinde, odaklanmış model yoksa komut devre dışı bırakılıyor. Hedefi etkin sahneye göre belirlenen eylemler için bu desteklenen bir yoldur. SwiftUI ile menü çubuğunu oluşturma ve özelleştirme.
Bu komut örneğinden, bir MenuBarExtra açmanın kendisine daha önce etkin olan düzenleyiciye güvenilir bir referans verdiği sonucunu çıkarmayın. Bu makaledeki panel kasıtlı olarak uygulama genelindeki durum üzerinde çalışır. Panelin tek bir düzenleyiciyi hedeflemesi gerekiyorsa hangi pencerenin eylemi alması gerektiğini varsaymak yerine, uygulama sahipliğindeki düzenleyici tanımlayıcılarının listesi gibi açık bir hedef seçimi tasarımı ekleyin.
AppKit veya TextKit’i yalnızca düzenleyici gerektiriyorsa ekleyin
Etiket düzenleyicisi yalnızca SwiftUI denetimlerine ihtiyaç duyar. Gereksinimler özel bir AppKit metin görünümünü kapsayacak şekilde genişlerse NSViewRepresentable, bir NSView’ı SwiftUI hiyerarşisinde oluşturmak, güncellemek ve kaldırmak için Apple’ın desteklediği köprüdür. Apple ayrıca temsilci ve hedef eylem iletilerini SwiftUI’ye aktarmak için bir koordinatör kullanılmasını açıklıyor. NSViewRepresentable.
Bu köprüyü eklerken sahiplik planını koruyun: her düzenleyicinin görünüm örneğini ve düzenleme durumunu kendi penceresiyle ilişkilendirin, paylaşılan içeriği ise yalnızca paylaşılması amaçlanan yerlerde uygulama modelinden aktarın. Bir koordinatör, değişiklikleri bir bağlama veya model eylemi üzerinden iletmelidir. Uygulama önlemi olarak, devam eden bir düzenlemenin gereksiz yere sıfırlanmaması için metin görünümünün içeriğini değiştirmeden önce gelen içeriği karşılaştırın.
TextKit 2 uygulamasında Apple, textLayoutManager değerini modern düzen motoruna erişim noktası olarak tanımlıyor. TextKit oturumu, TextKit 2 metin görünümünde eski layoutManager özelliğine erişmenin uyumluluk kipini tetikleyip görünümü TextKit 1’e geçirebileceğini ve otomatik olarak geri geçişin gerçekleşmeyeceğini açıklıyor. Bir metin görünümünü yalnızca incelemek amacıyla bu eski özelliği kullanmaktan kaçının. TextKit, TextKit ve metin görünümlerindeki yenilikler.
macOS 13 desteği gerekiyorsa aynı sahiplik düzenini koruyun ancak eski gözlemleme yolunu kullanın: @Published içeren ObservableObject, sahibi olan @StateObject ve alıcı @EnvironmentObject özellikleriyle .environmentObject. Apple’ın geçiş kılavuzu bu mekanizmaların yeni Observation yaklaşımındaki karşılıklarını açıklıyor. Gözlemleme sistemlerini değiştirirken amaçlanan paylaşım sınırlarını koruyun. Observable Object protokolünden Observable makrosuna geçiş.
Sahipliği, arayüzden beklenen davranışa göre seçin: paylaşılan etiket için uygulamanın sahip olduğu tek bir örnek, her tamamlanmamış taslak için pencerenin sahip olduğu tek bir değer ve bunları birbirine bağlayan açık bir eylem. Menü çubuğu paneli veya düzenleyici genişledikçe iki pencereli işlem dizisini gerileme kontrolü olarak kullanın.
Kaynaklar
- Uygulamanızdaki model verilerini yönetme — Apple
- WindowGroup — Apple
- MenuBarExtra — Apple
- pencere — Apple
- MainActor — Apple
- Bindable — Apple
- SwiftUI ile menü çubuğunu oluşturma ve özelleştirme — Apple
- NSViewRepresentable — Apple
- TextKit — Apple
- TextKit ve metin görünümlerindeki yenilikler — Apple
- Observable Object protokolünden Observable makrosuna geçiş — Apple