Güncellemeler sırasında SwiftUI sayfasını açık tutmak için sunum ana bilgisayarının kimliğini koruyun ve sunum bağlamasını etkin tutun. Değişen alt içerik sunucuyu değiştirmeden güncellenebilsin diye sayfayı kararlı bir kapsayıcıya ekleyin.

Bu kılavuz, temel arayüz değişirken görünür kalması gereken düzenlenebilir bir sayfaya sahip Mac uygulaması geliştirenler içindir. Uygulamalı örnekte yerel, sentetik metin kullanılır ve birbirinden bağımsız olarak inceleyebileceğiniz üç unsur ayrılır: sunum durumu, sunumu yapan görünümün ömrü ve düzenleyicinin içeriği.

Bir görünüm güncellendiğinde sayfa neden kaybolabilir?

SwiftUI, body değerlendirilirken yeni görünüm değerleri oluşturabilir ve görüntülenen görünümün kimliğini koruyabilir. Durum, bu kimliğin ömrü boyunca korunur. Görünümün kaldırılması veya kimliğinin değiştirilmesi bu ömrü sona erdirir; her iki dal da aynı görünüm türünü içerse bile koşullu dallar arasında geçiş yapmak farklı kimlikler oluşturabilir. Apple bu ilişkileri Demystify SwiftUI belgesinde açıklar.

Boole değerine dayalı bir sayfada, belgelenmiş sunum denetimi sheet(isPresented:onDismiss:content:) öğesine geçirilen bağlamadır: true değeri sunum isteğinde bulunur. Kaybolan bir sayfayı incelerken hem bu bağlamayı hem de değiştiriciyi taşıyan görünümün varlığını kontrol edin. Başka bir yerde saklanan true değeri, özgün sunum ana bilgisayarının varlığını sürdürdüğünü göstermez. Apple’ın Boole değerli sayfa belgelerine bakın.

Doğrudan bir başka neden, değişen açık bir tanımlayıcıdır. Apple, id(_:) öğesine verilen değerin değişmesinin, durum da dahil olmak üzere görünümün kimliğini sıfırladığını belgeliyor. Sunumu yapan görünümde ve üst öğelerinde revizyon sayaçlarından, düzenlenebilir metinden veya yeni oluşturulan değerlerden türetilmiş tanımlayıcılar olup olmadığını inceleyin. Sıfırlama davranışı istenmiyorsa bu tanımlayıcıları kaldırın. Bkz. id(_:).

Uygulamadaki çıkarım, sayfa değiştiricisini arayüzün değişen bölümünün dışına yerleştirmektir. Yükleme görünümü, filtrelenmiş bir satır veya alternatif bir ayrıntı düzeni kaldırılabiliyorsa, sunum sahibi olarak genellikle ona en yakın kalıcı üst görünümü kullanmak daha uygundur.

Tam bir Mac örneğiyle kararlı bir sayfa sahibi oluşturun

Xcode’da bir macOS SwiftUI uygulaması oluşturun, dağıtım hedefini macOS 12 veya üzeri olarak ayarlayın ve oluşturulan uygulama ile içerik görünümü bildirimlerini aşağıdaki tek dosyayla değiştirin. Yalnızca bir @main uygulama bildirimi bırakın. macOS 12 taban sürümü, örnekteki dismiss ortam eylemini destekler; TextEditor ise macOS 11’den itibaren kullanılabilir. Apple’ın dismiss belgelerine ve TextEditor belgelerine bakın.

Pencerenin kök görünümü sunum bayrağını, düzenlenebilir metni, revizyon sayacını ve düzen bayrağını yönetir. Dıştaki VStack, sayfa değiştiricisini taşır. Yalnızca bu kapsayıcının içindeki içerik dallar arasında değişir. Apple, durumun ihtiyaç duyan en üst görünümde özel olarak tanımlanmasını ve değiştirmesi gereken alt görünümlere bağlamalarla aktarılmasını öneriyor. Bkz. State.

import SwiftUI

@main
struct StableSheetDemoApp: App {
    var body: some Scene {
        WindowGroup {
            SheetOwnerView()
        }
    }
}

struct SheetOwnerView: View {
    @State private var isEditorPresented = false
    @State private var note = "Sample note Alpha"
    @State private var revision = 0
    @State private var compactLayout = false

    var body: some View {
        VStack(alignment: .leading, spacing: 16) {
            Text("Local Notes")
                .font(.title)

            if compactLayout {
                Text("Compact summary: revision \(revision)")
            } else {
                VStack(alignment: .leading, spacing: 8) {
                    Text("Expanded summary")
                    Text("Revision \(revision)")
                    Text(note)
                }
            }

            Button("Edit Note") {
                isEditorPresented = true
            }
        }
        .padding(24)
        .frame(minWidth: 440, minHeight: 260)
        .sheet(isPresented: $isEditorPresented) {
            NoteEditorSheet(
                note: $note,
                revision: $revision,
                compactLayout: $compactLayout
            )
        }
    }
}

struct NoteEditorSheet: View {
    @Binding var note: String
    @Binding var revision: Int
    @Binding var compactLayout: Bool
    @Environment(\.dismiss) private var dismiss

    var body: some View {
        VStack(alignment: .leading, spacing: 16) {
            Text("Edit Sample Note")
                .font(.headline)

            TextEditor(text: $note)
                .frame(minHeight: 160)

            Text("Owner revision: \(revision)")

            Button("Update Owner") {
                revision += 1
                compactLayout.toggle()
            }

            HStack {
                Spacer()
                Button("Done") {
                    dismiss()
                }
            }
        }
        .padding(24)
        .frame(width: 460, height: 340)
    }
}

Bu, belgelenmiş API’ler açısından incelenmiş eksiksiz bir örnek uygulamadır. Bu ortamda Swift araç zinciri veya macOS çalışma zamanı bulunmadığından burada derlenmemiş ya da çalıştırılmamıştır.

Sayfayı kapatmadan güncellemeyi deneme

  1. Uygulamayı çalıştırın ve Edit Note düğmesine tıklayın.
  2. Örnek metni Sample note Beta ile değiştirin.
  3. Update Owner düğmesine birkaç kez tıklayın. Her tıklama sahip durumunu artırır ve sahibin koşullu içeriğini değiştirir.
  4. Düzenleyicinin görünür kaldığını, metninin Sample note Beta olarak korunduğunu ve revizyon göstergesinin ilerlediğini doğrulayın.
  5. Done düğmesine tıklayın. Bu düzen etkinse pencerenin genişletilmiş düzende düzenlenen metni gösterdiğini doğrulayın.
  6. Düzenleyiciyi yeniden açın ve düzenlenen metnin hâlâ kullanılabilir olduğunu doğrulayın.

Güncelleme düğmesi sayfanın içindedir; böylece test, alttaki pencereye tıklamaya bağlı kalmaz. AppKit, sayfaları belge kipli olarak tanımlar: Bir sayfa görünürken, üst penceresine yöneltilen çoğu olaya izin verilmez. Sahibin bir bağlama aracılığıyla güncellenmesi, bu örneğin durum değişikliğini doğrudan sınamasını sağlar. Bkz. beginSheet(_:completionHandler:).

Düzenleyiciyi kapatmanın ne anlama gelmesi gerektiğine karar verin

Bu örnek, sahibin metnini anında düzenler. Done düzenleyiciyi kapatır; ayrı bir kaydetme işlemi gerçekleştirmez. Dize, pencereye özgü durumda kalır ve örnek uygulama açılışları arasında kalıcılık sağlamaz. Bu davranış, State belgesinde açıklanan paylaşılan bağlama tasarımına uygundur.

İptal edilebilir bir taslak gerekiyorsa ayrı bir taslak değeri kullanın ve bunu belgeye yalnızca açık bir onay eylemiyle kopyalayın. Kapatmayı uygulamadan önce bu davranışı tanımlayın: Taslağın kaydedilip kaydedilmeyeceğine karar vermek, sayfanın sahibi olan görünümü seçmekten ayrı bir konudur.

Kapanmayı birbirinden bağımsız üç denetimle teşhis edin

Kendi uygulamanızda aşağıdaki adımları izleyin. Bu adımlar, kaybolan her sayfanın aynı nedenden kaynaklandığını varsaymadan genel kimlik kurallarını odaklı bir incelemeye dönüştürür.

Denetim İncelenecekler Uygulanabilir düzeltme
Sunum değeri Boole değerini false yapan veya isteğe bağlı sunum öğesini temizleyen tüm yazma işlemleri Kapatma yazma işlemlerini amaçlanan kapatma eylemlerinde tutun
Sunucu kimliği Koşullu dallar, kaldırılan satırlar, değişen üst öğe tanımlayıcıları Hem sunum durumunu hem de değiştiriciyi uygun, kararlı bir üst görünüme taşıyın
İçerik ömrü Sayfa içi durum, değişen içerik tanımlayıcıları, düzenleyici eşitlemesi Amaçlanan düzenleme oturumunu koruyun ve mevcut içeriği bilinçli biçimde güncelleyin

Önce projenizde sunum özelliğine yapılan atamaları ve dismiss() çağrılarını arayın. Yenileme geri çağrısı sayfayı açıkça kapatıyor olabilir. Örnekte olduğu gibi dismiss öğesini sayfanın kendi görünümünde okuyun: Apple, eylemin alındığı ortam için geçerli olduğunu belgeliyor. Bunu pencerenin kök görünümünde okumak, bunun yerine o pencereyi hedefleyebilir. Bkz. dismiss.

İkinci olarak, değiştiricinin üzerindeki hiyerarşiyi inceleyin. Sayfa, yenileme sırasında kaldırılan bir ayrıntı görünümüne eklenmişse, onu bu değiştirme sınırının üzerine taşıyın. Yerel olarak yönetilen sunum durumunu da taşıyın. Kimliği sona eren bir alt görünümde bayrağı tutmak, Apple’ın görünüm ömrü açıklamasına göre bu depolamayı değiştirilmeye açık bırakır.

Üçüncü olarak, kapanmış bir sunumu, görünür kalmasına karşın taslağını kaybeden bir düzenleyiciden ayırın. Önce düz TextEditor örneğiyle başlayın, sonra özel içeriğinizi aşamalı olarak ekleyin. Bu karşılaştırma bir teşhis yöntemidir: Sorunun sunum sahipliğini mi yoksa içerik güncelleme mantığınızı mı izlediğini belirlemeye yardımcı olur.

Öğe tabanlı sayfaya geçmeli misiniz?

Sunumun kaynağı belirli bir seçili varlıksa sheet(item:onDismiss:content:) kullanın. Apple, Identifiable uyumlu isteğe bağlı bir öğeyi belgeler, içerik kapanışına nil olmayan bir öğe aktarır ve öğe değiştiğinde sunumun yenileneceğini belirtir. Aşırı yüklemeler arasında geçiş yapmak, ömrü sona eren bir sunucuyu tek başına korumaz. Bkz. öğe tabanlı sayfalar.

Amaçlanan düzenleme oturumu boyunca varlığı temsil eden bir tanımlayıcı seçin. Swift’in Identifiable protokolü kararlı kimliği ifade eder ve belgeleri, gereken süreyi ve kapsamı katılımcı türlerin belirlemesine açıkça bırakır. Bir özellik her okunduğunda yeni bir tanımlayıcı üretmek, varlığın devamlılığını temsil edemez. Bkz. Identifiable.

Örnekteki gibi basit bir düzenleyicide Boole sunum bayrağı ile ayrı metin bağlamaları, amaçlanan ilişkiyi incelemeyi kolaylaştırır. Seçili öğeyi düzenleyen bir görünümde, seçim kimliğini sık değişen alanlardan ayrı tutun. Başka bir varlığın seçilmesini bilinçli bir sunum kararı olarak ele alın ve önce mevcut taslağa ne olacağını tanımlayın.

Etkileşimli kapatmayı devre dışı bırakmak sayfayı açık tutar mı?

interactiveDismissDisabled(_:), programatik olmayan kapatmayı denetler. Apple, bunun sunum bağlamasını değiştirmekten veya dismiss çağrısından kaynaklanan kapanmayı önlemediğini açıkça belirtiyor. Bu nedenle belgelenmiş rolü, kimlik değiştirmenin çözümü değildir. Bkz. interactiveDismissDisabled(_:).

Bu kapatma davranışına arayüzünüz ihtiyaç duyduğunda değiştiriciyi yalnızca sayfa hiyerarşisinin içinde uygulayın ve açık bir tamamlama yolu bırakın. Bu görev için önce ana bilgisayarı ve durumunu koruyun. Nedeni belirlemeden kapatma kısıtlaması eklemek, sahiplikle ilgili asıl sorunu çözmeden bırakabilir.

Sayfa AppKit veya TextKit içeriği içerdiğinde ne değişir?

NSViewRepresentable ile gömülen bir NSTextView için oluşturma ile güncellemeyi birbirinden ayırın. Apple, makeNSView(context:) öğesini oluşturma adımı; updateNSView(_:context:) öğesini ise sonraki SwiftUI bilgilerini uygulama yeri olarak tanımlıyor. Bir koordinatör, temsilci veya hedef-eylem değişikliklerini SwiftUI’ye geri aktarır. Bkz. NSViewRepresentable ve makeNSView(context:).

Bu yaşam döngüsünden çıkarılan yararlı bir uygulama kuralı, ilgisiz sahip durumu değişti diye düzenleyiciyi yeniden oluşturmaktan kaçınmaktır. İçeriği değiştirmeden önce, gelen model metninin gerçekten farklı olup olmadığını ve eşzamanlı yerel düzenlemelerin nasıl ele alınacağını belirleyin. Kararlı bir sayfa, metin üzerinde çakışan yazma işlemlerini tek başına çözmez.

TextKit, metin depolama, düzen ve işleme olanağı sağlar. AppKit’in NSTextView sınıfı bu özellikleri bir araya getirir ve modern ve eski düzen motorlarını sunar. Bu altyapı düzenleyicinin içeriğiyle ilgilidir; sunum sahipliğinin yine de kendi kararlı ömrüne ihtiyacı vardır. Düzenleme görevi gerektiriyorsa TextKit kullanın ve metin güncelleme davranışını sayfanın kapanmasından ayrı inceleyin. Apple’ın TextKit genel bakışına bakın.

Pencere sahibi AppKit sunumu ne zaman uygundur?

Mevcut uygulamanız pencere yaşam döngüsünü zaten AppKit’te yönetiyorsa, macOS 26 SwiftUI içeriğini doğrudan o pencerede sunmak için NSWindow.beginSheet(content:completionHandler:) özelliğini sunar. Apple, sayfanın dismiss ortam eylemini, boyutlandırmayı, araç çubuklarını ve etkileşimli kapatma denetimini desteklediğini belgeliyor. Bu özelliği benimsemeden önce macOS 26 SDK’sı kullanın ve macOS 26 kullanılabilirlik sınırını uygulayın. Bkz. beginSheet(content:completionHandler:).

Döndürülen NSWindow.HostingSheetRepresentation, sunulan kök görünümün değiştirilmesini veya AppKit’ten programatik olarak kapatılmasını destekleyebilir. Pencere düzeyinde sahipliğin uygulamanın mimarisine uyduğu durumlarda belgelenmiş bir seçenek sunar. Yukarıdaki kararlı SwiftUI kapsayıcı örneği için gerekli değildir. Bkz. NSWindow.HostingSheetRepresentation.

Daha eski macOS sürümlerinde AppKit’in mevcut beginSheet(_:completionHandler:) özelliği bir NSWindow sayfasını kabul eder ve sunum kuyruğunu belgeler. Daha yeni SwiftUI içerik aşırı yüklemesinin bu sistemlerde kullanılabildiğini varsaymayın. Bkz. beginSheet(_:completionHandler:).

Sahibi kararlı tutun, sunum değerini etkin bırakın ve düzenleyicinin amaçlanan doğruluk kaynağını koruyun. Daha karmaşık içeriği geri yüklemeden önce bu koşulları küçük bir yerel güncelleme eylemiyle doğrulayın. Böylece Apple’ın belgelediği ömür ve API sınırları içinde, arayüz güncellenirken düzenlemeyi sürdürebilirsiniz.

Kaynaklar