Karartılmış şekilleri, ana hatları ve sürükleme etiketlerini sürükleme kaynağının oturum sonu geri çağrısından geri yükleyin. Hedefin çıkış geri çağrısı yalnızca işaretçinin o hedeften ayrıldığı anlamına gelir. Mac uygulamasında, kullanılabiliyorsa SwiftUI’nin kaynak oturumu API’sini kullanın ya da sürüklemeyi bir AppKit kaynağının yönetmesini sağlayın.

Bu kılavuz tek bir görevi ele alıyor: kullanıcı sürüklemeyi bitirdikten sonra sentetik bir şekli, hiçbir hedef kabul etmese bile, normal görünümüne döndürmek. Örnek içeriğini bellekte tutar ve kabul edilen bırakmayı kopyalama olarak ele alır; bu nedenle geri bildirimi geri yüklemek kaynağı silmeyi veya taşımayı gerektirmez.

Bırakma hedeflerinin dışındayken sürükleme geri bildirimi neden etkin kalıyor?

Yaygın bir uygulama, sürükleme başladığında bir isDragging bayrağını ayarlar ve performDrop içinde temizler. Böylece temizlik, yalnızca kabul eden bir hedefin işleyebileceği bir olaya bağlanır. Pencerenin boş alanına bırakıldığında kaynağı sıfırlamaktan sorumlu hiçbir hedef kalmaz.

Apple, DropDelegate türünü, bırakmaları kabul edecek şekilde değiştirilmiş bir görünüm için arayüz olarak tanımlar. dropExited(info:) yöntemi, doğrulanmış bir sürüklemenin o görünümden çıktığını bildirir. Sürükleme başka bir hedefe devam edebilir, ilk hedefe geri dönebilir ya da başka bir yerde sona erebilir. Bu nedenle hedeften çıkış, kaynak oturumunun sona erdiğini gösteremez. DropDelegate

Aynı ayrım dropDestination(for:action:isTargeted:) için de geçerlidir: isTargeted, işaretçinin hedef alanının içinde olup olmadığını belirtir. Bunu hedefin kenarlığı veya arka planı için kullanın. Kaynağın karartılmasını veya sürükleme etiketini kaynak oturumu geri çağrısının yönetmesini sağlayın. dropDestination(for:action:isTargeted:)

Bu belgelenmiş sınırlar kullanışlı bir tasarım kuralı ortaya koyar: her görsel durumu, onu güvenilir biçimde sona erdirebilecek olayın sahibine atayın.

Geri bildirim veya sonuç Sorumlu Sıfırlama veya uygulama noktası
Karartılmış kaynak şekli Sürükleme kaynağı Kaynak oturumu sona erdiğinde
Vurgulanmış hedef Bırakma hedefi İşaretçi çıktığında; yerel bir sürüklemede kaynak tamamlandığında da ek güvenlik temizliği yapılır
Kabul edilen sentetik öğe Hedef/model Bırakma doğrulanıp başarılı olduğunda
Geçici dışa aktarma kaynakları Aktarım yaşam döngüsü Uygun olduğunda aktarım tamamlandığında

Mac uygulaması hangi desteklenen yaklaşımı kullanmalı?

Apple’ın güncel başvuru belgesi, onDragSessionUpdated(_:) öğesinin macOS 26 ve sonraki sürümlerde kullanılabildiğini belirtiyor. Bu öğe, alt görünüm hiyerarşisindeki en yakın sürükleme oturumuna ilişkin güncellemeleri izler. Kapsamı açık tutmak için bunu doğrudan kaynağın sürükleme değiştiricisinin dışına ekleyin. Yöntemi gerçekten bildiren bir SDK kullanın; çalışma zamanı kullanılabilirliği, SDK’de bulunmayan bir bildirimin derlenmesini sağlamaz. onDragSessionUpdated(_:)

Belgelerde ele alınması gereken dar kapsamlı bir tutarsızlık var: değiştiricinin örneğinde konum ve işlem bağımsız değişkenleri içeren sona ermiş bir aşama gösterilirken, aşamaya ayrılmış başvuru ended(DropOperation) öğesini bildiriyor. Aşağıdaki örnek, görsel geri yükleme ilişkili değerleri gerektirmediği için .ended ile bu değerleri çıkarmadan eşleşiyor. İşleme özel davranış eklemeden önce kurulu SDK’nızdaki bildirimi inceleyin. DragSession.Phase.ended(_:)

Dağıtım hedefiniz veya SDK’niz SwiftUI geri çağrısını kullanamıyorsa, AppKit sürükleme kaynağı belgelenmiş tamamlanma sınırını sağlar. Apple, sürükleme sona erdiğinde veya iptal edildiğinde kaynağın açıkça draggingSession(_:endedAt:operation:) aldığını belirtiyor. Özel bir kaynak bu oturumu kendisi başlatmalıdır. beginDraggingSession(with:event:source:)

Yalnızca bir şekli tek bir tuval içinde yeniden konumlandırmanız gerekiyorsa, sistem sürükle-bırak oturumu yerine yerel bir DragGesture kullanmayı değerlendirin. @GestureState, hareket etkin olmadığında sıfırlanır; bu nedenle o hareketin geçici geri bildirimi için uygundur. Bu seçimi, hedeflere aktarılabilir içerik sağlayan sistem sürüklemesinden ayrı tutun. GestureState

SwiftUI’de kaynağın yönettiği temizliği uygulama

onDragSessionUpdated(_:) öğesini sunan bir SDK ile macOS SwiftUI uygulaması oluşturun, dağıtım hedefini macOS 26 veya üstüne ayarlayın ve oluşturulan uygulama ile içerik görünümü bildirimlerini bu eksiksiz programla değiştirin. Program, shape.circle adlı tek bir sentetik dize ve tek bir alıcı alan sağlar.

Kaynak, aktarılabilir içerik sağlamak için draggable(_:) kullanır. Hedef tam olarak eşleşen tek bir dizeyi kabul eder, diğer girdiler için false döndürür. Apple, hedefin Boolean sonucunun bırakmanın başarılı olup olmadığını belirttiğini belgeliyor. draggable(_:), dropDestination(for:action:isTargeted:)

import SwiftUI

@main
struct DragFeedbackDemoApp: App {
    var body: some Scene {
        WindowGroup {
            DragFeedbackDemo()
        }
    }
}

struct DragFeedbackDemo: View {
    private let payload = "shape.circle"

    @State private var isDragging = false
    @State private var isTargeted = false
    @State private var acceptedCopies = 0

    var body: some View {
        VStack(spacing: 24) {
            Text("Drag the circle into the receiving area")

            Circle()
                .fill(Color.blue)
                .frame(width: 72, height: 72)
                .opacity(isDragging ? 0.4 : 1)
                .draggable(payload)
                .onDragSessionUpdated { session in
                    switch session.phase {
                    case .initial, .active:
                        isDragging = true
                    case .ended, .dataTransferCompleted:
                        restoreFeedback()
                    default:
                        break
                    }
                }

            Text(isDragging ? "Dragging" : "Ready")

            RoundedRectangle(cornerRadius: 12)
                .fill(isTargeted
                      ? Color.blue.opacity(0.2)
                      : Color.gray.opacity(0.12))
                .overlay {
                    Text("Receiving area")
                }
                .frame(width: 280, height: 120)
                .dropDestination(for: String.self) { items, _ in
                    guard items.count == 1,
                          items.first == payload else {
                        return false
                    }
                    acceptedCopies += 1
                    return true
                } isTargeted: { targeted in
                    isTargeted = targeted
                }

            Text("Accepted copies: \(acceptedCopies)")
        }
        .padding(32)
        .frame(minWidth: 380, minHeight: 400)
    }

    private func restoreFeedback() {
        isDragging = false
        isTargeted = false
    }
}

İki bayrağın görevleri farklıdır. isDragging kaynağın opaklığını değiştirir. isTargeted alıcı alanın dolgusunu değiştirir. Bu nedenle alıcı alana giriş ve çıkış, sürükleme devam ederken kaynağı yanlışlıkla normal görünümüne döndüremez.

Aşama başvuru belgesi, initial öğesini başlamak üzere olan oturum, active öğesini hareket ve ended öğesini tamamlanma olarak tanımlar. Örnek geri bildirimi ended aşamasında geri yükler; ayrıca dataTransferCompleted aşamasında aynı sıfırlamayı güvenle yinelenebilir hâle getirir. Başarısız bir sürüklemenin geri bildirimini geri yüklemek için yalnızca aktarımın tamamlanmasını beklemez. DragSession.Phase

Apple, dataTransferCompleted öğesini sürüklenen öğelerin aktarıldığı ve geçici öğelerin temizlenebileceği an olarak tanımlar. Bu örnekte geçici dışa aktarma kaynağı yoktur. Daha büyük bir uygulamada, opaklığı geri yüklemenin aktarımın hâlâ ihtiyaç duyduğu bir öğeyi silmemesi için görsel temizliği kaynak temizliğinden ayrı tutun. DragSession.Phase.dataTransferCompleted

Daire, oturum boyunca görünüm hiyerarşisinde kalır. Kabul edilen bir bırakma hedefin sayısını artırır; kabul edilmeyen bir bırakma içeriği değiştirmez. Böylece örnek, geri bildirimi yeniden sıralama veya taşıma semantiğinden bağımsız olarak incelemek için kullanışlıdır. Kod, alınan bildirimlere göre incelendi; kullanılabilir ortam SwiftUI içermeyen Linux olduğu için derlenmedi veya çalıştırılmadı.

AppKit yedeği olay sahipliğini nasıl korumalı?

Desteklenen daha eski bir macOS sürümü için sürükleme kaynağı uygulamasını NSViewRepresentable aracılığıyla barındırılan bir NSView ile değiştirin. Temsilci yerel görünümü oluşturup günceller ve geri çağrılarını SwiftUI durumuna bağlar. SwiftUI, temsil edilen görünümün yerleşimini denetler; bu nedenle temsil edilen görünümün çerçevesini kendiniz atamak yerine SwiftUI boyutlandırma değiştiricilerini kullanın. NSViewRepresentable

Yedeği, kaynağın yönettiği tek bir sıra olarak uygulayın:

  1. Yerel kaynak görünümünün mouseDown(with:) içinde, bu görünüme iletilen gerçek fare aşağı olayını saklayın.
  2. mouseDragged(with:) içinde seçtiğiniz sürükleme eşiğini uygulayın ve ikinci bir oturum başlatılmasını engelleyin.
  3. Sentetik öğe tanımlayıcısını taşıyan, bellekteki bir pano yazıcısından NSDraggingItem oluşturun.
  4. Oluşturulmuş bir şekil görüntüsü sağlayın ve sürükleme çerçevesini kaynak görünümün koordinat sisteminde ayarlayın.
  5. Saklanan fare aşağı olayını ve NSDraggingSource uygulayan nesneyi kullanarak beginDraggingSession(with:event:source:) öğesini çağırın.
  6. Kaynak geri bildirimini draggingSession(_:willBeginAt:) aşamasında gösterin ve draggingSession(_:endedAt:operation:) aşamasında koşulsuz olarak geri yükleyin.
  7. İzleme tamamlandığında saklanan giriş olayını ve oturum kaydını temizleyin. Oturum tamamlanana kadar kaynağı ve geri çağrı sahibini canlı tutun.

Bu ayrıntılar AppKit’in açık sözleşmesini izler: oturum başlatma yöntemi başlatıcı fare aşağı olayını alır, sürükleme öğesi çerçeveleri görünümün koordinat sistemini kullanır ve kaynak yaşam döngüsü iletilerini alır. NSDraggingItem, NSPasteboardWriting protokolüne uyan bir nesneyi kabul eder; tek görüntü alan kolaylık yöntemi sürükleme çerçevesini ve içeriğini ayarlar. beginDraggingSession(with:event:source:), init(pasteboardWriter:), setDraggingFrame(_:contents:)

Bu uygulama içi etkileşim için NSDraggingContext.withinApplication durumunda .copy, diğer bağlamlarda ise boş bir işlem kümesi döndürün. Bu, yalnızca yerel kopyalar sunmaya yönelik açık bir tercihtir. AppKit, kaynağın hangi işlemlere izin verdiğini draggingSession(_:sourceOperationMaskFor:) aracılığıyla sorar. draggingSession(_:sourceOperationMaskFor:)

Görünümü yalnızca başarılı kopyalarda değil, her tamamlanma sonucunda geri yükleyin. Model değişikliklerini uygulamanızın gerçekten gerçekleştirdiği işleme bağlı tutun. Tamamlanma geri çağrısındaki nokta ekran koordinatlarındadır; bunu doğrudan SwiftUI hedefinin yerel sınırlarıyla karşılaştırmayın. draggingSession(_:endedAt:operation:)

Mevcut bir SwiftUI .draggable öğesinin arkasına yerleştirilen şeffaf bir yerel görünüm, yalnızca NSDraggingSource protokolüne uyarak o oturumun kaynağı hâline gelmez. Kaynak, oturum başlatıldığında iletilen nesnedir. Başlatma ve tamamlanma için tek bir sahip seçin; yerel sürükleme yüzeyini şeklin amaçlanan etkileşim alanıyla sınırlı tutun.

Fare bırakma izlemeyi veya sağlayıcının bellekten çıkarılmasını neden kullanmamalı?

Uygulama çapında fare bırakma izleyicisi, sürükleme tamamlanmasından farklı bir sınır olan giriş dağıtımını gözlemler. Apple, yerel olay izleyicilerinin iç içe izleme döngüleri tarafından tüketilen olayları almadığını belgeliyor. Bu sınırlama, izleyiciyi kaynağın belgelenmiş tamamlanma geri çağrısı yerine kullanmayı uygunsuz kılar. addLocalMonitorForEvents(matching:handler:)

Benzer şekilde, bir öğe sağlayıcısının deinit bildirimi nesnenin yaşam süresini belirtir. Swift’te bir örnek bellekten çıkarılmadan hemen önce sınıf yıkıcısı çağrılır; bu sözleşme sürüklemenin ne zaman sona erdiğini belirlemez. Sağlayıcının yok edilmesini tamamlanma sinyali olarak kullanmak, kullanıcı arayüzü durumunu sürükleme yaşam döngüsü yerine referans sahipliğine bağlar. Deinitialization

Sistem sürüklemesinin yanına ikinci bir DragGesture eklemek de aynı kavramsal sorunu yaratır: bu hareketin durumu o harekete aittir. Yerel hareket etkileşimi yönetiyorsa @GestureState kullanın; sistem sürüklemesi yönetiyorsa sürükleme kaynağının oturum geri çağrısını kullanın. GestureState

Düzeltmeyi bir tamamlanma matrisiyle doğrulama

Her yolu ayrı ayrı denetlemek için sentetik daireyi kullanın. Bunlar, bildirilen test sonuçları değil, uygulama için beklenen davranışlardır.

Etkileşim Beklenen kaynak geri bildirimi Beklenen hedef sonucu
Daireyi alıcı alanın içine bırakın Normale döner Sayı bir artar
Pencerenin boş alanına bırakın Normale döner Sayı değişmez
Sürükleme devam ederken alıcı alandan çıkın Karartılmış kalır Hedef vurgusu temizlenir
Yeniden girip geçerli bir bırakmayla tamamlayın Tamamlanınca normale döner Sayı bir artar
Kabul eden bir hedef olmadan uygulama penceresinin dışına bırakın Normale döner Sayı değişmez
Etkin bir sürüklemeyi iptal edin Normale döner Sayı değişmez
Birkaç başarısız sürüklemeyi tekrarlayın Her oturum normal biçimde geri yüklenir Kabul edilen kopyalar birikmez

Kaynak karartılmış kalıyorsa önce tamamlanma gözlemcisinin gerçek sürükleme kaynağını sardığını ve kaynağın görünür kalmayı sürdürdüğünü kontrol edin. Yalnızca hedef vurgusu takılı kalıyorsa hedefin giriş/çıkış işlemesini ve paylaşılan temizliği inceleyin. Kabul edilen sayı reddedilen bir bırakmada değişiyorsa doğrulamayı ve model değişikliğini görsel geri yüklemeden ayrı inceleyin.

Kalıcı çözüm, tamamlanma sahipliğini açıkça belirlemektir: kaynak oturum genelindeki geri bildirimi geri yükler, hedef kendi üzerine gelme durumunu ve kabul edilen içeriği yönetir, aktarım temizliği de belgelenmiş yaşam döngüsünü izler. Bu sorumluluklar ayrıldığında, her hedefin dışına bırakmak görsel durumu hedef geri çağrısını bekler hâlde bırakmaz.

Kaynaklar