Mac uygulamanızın içinde sürüklenen bir şeklin ofsetini hareket sırasında doğrudan güncelleyin, ardından bırakıldığında aynı ofsete yay animasyonu uygulayın. macOS 14 ve sonraki sürümlerde SwiftUI, izlenen sürükleme hızını otomatik olarak yaya aktarabilir.

Bu öğretici küçük bir masaüstü etkileşimi oluşturur: sentetik mavi bir kutucuğu herhangi bir yönde sürükleyin, bırakın ve başlangıç konumuna geri yaylanmasını sağlayın. Tüm durum bellekte kalır ve hareket, uygulamanın kendi penceresindeki kutucuğa aittir.

Yay, sürüklemenin son hızını nasıl devralır?

SwiftUI’ın Transaction.tracksVelocity özelliği, animasyonlanabilir özellikler değişirken hızı kaydeder. Apple, hareket onChanged ve updating geri çağrılarının bu izlemeyi otomatik olarak etkinleştirdiğini belgeliyor. Etkileşim sona erdiğinde bir animasyon birikmiş hızı kullanabilir. Bu nedenle, bu sıradan SwiftUI etkileşimi için başlangıç hızını hesaplayıp aktarmanız gerekmez. Apple’ın hız izleme belgeleri bu aktarımı açıkça anlatır.

Önemli bağlantı, animasyonlanan özelliktir. Sürükleme sırasında hareketin ötelemesini kutucuğun ofsetine yazın. Bırakıldığında, aynı ofseti withAnimation içinde hedef konumuna değiştirin. Görünümü ve durumunu canlı tutmak, SwiftUI’a izleyebileceği kesintisiz bir özellik sağlar.

Bu uygulamada sürükleme güncellemelerini animasyonsuz tutun. Apple, işlem hızı izlemesiyle kendi hızını yöneten bir animasyonun aynı anda kullanılamayacağını açıklar. Sürükleme sırasında etkileşimli yay kullanmak da desteklenen bir tekniktir, ancak kutucuğun işaretçiyi takip etme biçimini değiştirir; burada kullanılan doğrudan takip etkileşimi için gerekli değildir. İşlem hızı izleme

Yayın hedefi ile başlangıç hareketi farklı işlevlere sahiptir. Hedef, kutucuğun nerede durması gerektiğini belirtir. Devralınan hız ise bırakıldığında nasıl hareket ettiğini gösterir. Ev konumundan uzaklaşırken bırakılan bir kutucuk, yay onu geri getirmeden önce dışarı doğru hareketini sürdürebilir. Apple, Yaylarla animasyon oluşturma belgesinde hareketin hareket hızıyla sürdürülmesini gösteriyor.

Eksiksiz bir macOS sürükle ve bırakma örneği oluşturun

Xcode’da SwiftUI kullanan bir macOS uygulaması oluşturun, dağıtım hedefini macOS 14.0 veya sonraki bir sürüme ayarlayın ve oluşturulan uygulama giriş dosyasını aşağıdaki kodla değiştirin. Hedefte yalnızca bir @main uygulama bildirimi bulundurun. Örnek, macOS 14 animasyon tamamlama API’sini kullanır. Apple’ın animasyon tamamlama belgeleri

Bu, varlık veya paket bağımlılığı olmayan, tek dosyalık eksiksiz bir uygulamadır. API imzaları belgelere göre kontrol edildi; masaüstü uygulaması bu araştırma ortamında derlenmedi veya çalıştırılmadı.

import SwiftUI

@main
struct SpringReleaseDemoApp: App {
    var body: some Scene {
        WindowGroup {
            SpringReleaseView()
        }
    }
}

struct SpringReleaseView: View {
    @State private var offset: CGSize = .zero
    @State private var isReturning = false
    @GestureState private var isDragging = false

    var body: some View {
        VStack(spacing: 16) {
            Text("Drag the tile, then release")
                .font(.headline)

            ZStack {
                RoundedRectangle(cornerRadius: 20)
                    .stroke(.gray.opacity(0.4), lineWidth: 2)
                    .frame(width: 112, height: 112)

                RoundedRectangle(cornerRadius: 20)
                    .fill(.blue)
                    .frame(width: 112, height: 112)
                    .contentShape(Rectangle())
                    .offset(offset)
                    .gesture(tileDrag)
                    .allowsHitTesting(!isReturning)
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .coordinateSpace(name: "canvas")
        }
        .padding(24)
        .frame(width: 640, height: 440)
        .onChange(of: isDragging) { _, active in
            guard !active, !isReturning else { return }

            // Cancellation cleanup has no successful release value.
            withTransaction(Transaction(animation: nil)) {
                offset = .zero
            }
        }
    }

    private var tileDrag: some Gesture {
        DragGesture(
            minimumDistance: 3,
            coordinateSpace: .named("canvas")
        )
        .updating($isDragging) { _, active, _ in
            active = true
        }
        .onChanged { value in
            // Gesture callbacks automatically track velocity.
            offset = value.translation
        }
        .onEnded { _ in
            returnHome()
        }
    }

    private func returnHome() {
        guard !isReturning else { return }
        isReturning = true

        withAnimation(
            .spring(duration: 0.45, bounce: 0.2),
            completionCriteria: .removed
        ) {
            offset = .zero
        } completion: {
            isReturning = false
        }
    }
}

Hareket, hareket eden kutucuğa eklenirken adlandırılmış koordinat alanı sabit tuvale aittir. Bu, etkileşimde kullanılan koordinat seçimini açık hale getirir. Apple’ın DragGesture belgeleri, yapılandırılmış bir sürüklemeyi hareket değiştiricisiyle eklemeyi açıklar.

value.translation, sürükleme başladığından beri oluşan toplam yer değiştirmedir. Bunu offset özelliğine atayın; önceki ofsete tekrar tekrar eklemeyin. Tekrarlı toplama, aynı hareketin birden çok kez birikmesine neden olur. Bu örnekte kabul edilen her sürükleme ev konumundan başladığından ayrı bir başlangıç ofsetine gerek yoktur. Apple’ın öteleme tanımı

Başarılı bırakma işlemi hemen returnHome() işlevini çağırır. Önce ofseti sıfırlamaz veya yayı daha sonraya planlamaz. Böylece izlenen sürükleme değişiklikleri ile bırakma animasyonu aynı durum özelliği üzerinden bağlantılı kalır.

Sürükleme, konum ve geri dönüş durumlarını neden ayrı tutmalı?

Üç özellik farklı soruları yanıtlar: offset istenen yer değiştirmeyi saklar, isDragging etkin bir hareketi izler ve isReturning örneğin geri dönüş aşamasını kaydeder. Kutucuğun sahibi bu değerleri yerel olarak tutar. Apple, görünüme ait durumun private olarak bildirilmesini ve bağlamaların yalnızca başka bir görünümün bu durumu değiştirmesi gerektiğinde paylaşılmasını önerir. State belgeleri

Sarmalayıcı sözdiziminin de belirli bir amacı vardır. Swift özellik sarmalayıcıları, sarılmış değere erişimi yönetebilir ve dolar işareti önekiyle bir izdüşümlü değer sunabilir. Burada $isDragging, updating tarafından kullanılan hareket durumu izdüşümünü sağlar; bağımsız olarak tutulan ikinci bir bayrak değildir. Swift Programlama Dili: Özellikler

İptal işlemi ayrıca ele alınmalıdır, çünkü onEnded yalnızca hareket başarıyla tamamlandığında çalışır. Apple, nasıl sona erdiğinden bağımsız olarak sıfırlanması gereken durumlar için @GestureState kullanılmasını önerir. Örnek bu sıfırlamayı gözlemler ve normal bir geri dönüş devam etmiyorsa sentetik kutucuğu başlangıç konumuna getirir. Bu yedek davranış kutucuğu aniden başlangıç konumuna taşır; iptal edilen bir hareket için son bırakma hızının bulunduğunu iddia etmez. Hareket tamamlama davranışı, GestureState

Örnek, geri dönüş animasyonu bitene kadar yeni bir sürüklemeyi de bilerek kabul etmez. Model ofsetini sıfıra ayarlamak animasyonu başlatır; dolayısıyla kutucuk ekranda hâlâ hareket ederken sıfır, istenen hedef konumdur. Bu kısıtlamayı kaldırmak, yeni bir sürüklemenin ekranda o anda görünen konumdan nasıl devralacağını tasarlamayı gerektirir.

allowsHitTesting(!isReturning), kutucuğun isabet testine katılıp katılmayacağını denetler. .removed tamamlama ölçütü, animasyon tamamen bittiğinde kısıtlamayı kaldırır. Böylece yayın süre parametresinden bir gecikme tahmin etmeye gerek kalmaz. İsabet testi denetimi, Animasyonun kaldırılma tamamlanması

Bırakma nasıl ayarlanmalı ve kontrol edilmeli?

Sabit bir hedef ve sabit yay ayarlarıyla başlayın. Animasyon parametrelerini değiştirmeden önce kutucuğu bırakma biçiminizi değiştirin. Böylece hız aktarımını yay ayarlarından ayrı olarak değerlendirebilirsiniz.

Pencere içinde şu kontrolleri yapın. Bunlar belgelenen aktarıma dayalı bir tanılama sırasıdır; bu örnekten alınmış ölçüm sonuçları değildir.

Kontrol Eylem Bakılacak nokta
Neredeyse hareketsiz bırakma Ev konumundan uzağa sürükleyin, duraklayın ve bırakın. Geri dönüş, çok az devralınmış hareketle başlamalıdır.
Dışa doğru bırakma Ev konumundan uzağa sürükleyin ve hâlâ dışa doğru hareket ederken bırakın. Yay, geri dönmeden önce dışa doğru hareketi sürdürmelidir.
İçe doğru bırakma Uzağa sürükleyin, ev konumuna doğru yön değiştirin ve bırakın. Geri dönüş, içe doğru hareketi sürdürmelidir.
Çapraz bırakma Her iki eksen boyunca hareket ederken bırakın. Aktarımın yatay eksenle sınırlı bir dönüş gibi davranmak yerine yönü koruduğunu kontrol edin.
Sonraki etkileşim Geri dönüşün bitmesini bekleyin, ardından yeniden sürükleyin. Yeni sürükleme ev konumundan başlamalıdır.

Hızı ayarlamak için duration, yayın karakterini ayarlamak için bounce değerini kullanın. Örnekteki 0.45 ve 0.2 değerleri örnek başlangıç değerleridir, önerilen ayarlar değildir. Apple, süreyi algısal süre olarak tanımlar ve sıfır sıçramayı kritik sönümlü olarak açıklar; pozitif sıçrama değerleri yayı giderek daha fazla sıçramalı hale getirir. Yay yapılandırması

Algısal süre, görünür her hareketin ne zaman biteceğine ilişkin kesin bir vaat değildir. Apple bunu, yayın hareketine bağlı olan yerleşme süresinden ayırır. Bu örneğin geri dönüş aşaması için 0.45 değerini zamanlayıcı gibi kullanmak yerine tamamlama geri çağrısını kullanın. Yaylarla animasyon oluşturma

Bırakma hareketi yok sayıyor gibi görünüyorsa önce aktarımı inceleyin: Hareket, yayın animasyonladığı ofsetin aynısını mı değiştiriyor? Başka bir kod bırakmadan önce bu ofsetin üzerine yazıyor mu? Görünüm değiştiriliyor mu? Bir animasyon değiştiricisi sürükleme güncellemelerini animasyonluyor mu? Bu kontroller, animasyonlanabilir özelliklerin animasyona aktarılmadan önce değişirken izlenmesi gerektiğine ilişkin belgelenmiş gereklilikten kaynaklanır.

Hızı açıkça ne zaman okumalı veya AppKit kullanmalısınız?

DragGesture.Value.velocity, mevcut sürükleme hızını sunar ve onEnded, başarılı tamamlanmanın ardından hareketin son değerini sağlar. Hızın, iki sentetik yapışma konumu arasında seçim yapmak gibi bir uygulama kararını etkilediği durumlarda bu değeri okumak yararlıdır. Bu örnekteki sabit geri dönüş hedefi için otomatik izleme animasyon aktarımını zaten sağlar. Sürükleme hızı, Son hareket değeri

Açık bir yay başlangıç hızı API’si seçerseniz, hareket ölçümünü aktarmadan önce bu API’nin temsil biçimini kontrol edin. Apple’ın hareketle yönlendirilen animasyon gösterimi, kalan hareket mesafesine bölerek hızı birim hızına dönüştürür. Örnek hesaba göre saniyede 300 noktalık hız ve 150 noktalık kalan mesafe, saniyede 2 animasyon mesafesine dönüşür. Kalan mesafenin sıfır olduğu durumlar için de bilinçli bir politika gerekir. Burada otomatik SwiftUI yolu bu hesaplamayı önler. Kullanıcı arayüzü animasyonlarınızı ve geçişlerinizi geliştirin

Uygulamanıza ait mevcut bir AppKit görünümünün veya tanıyıcısının etkileşimi yönetmesi gerekiyorsa AppKit kullanın. NSPanGestureRecognizer.velocity(in:), sağlanan görünüme göre saniyede nokta cinsinden yatay ve dikey hız döndürür. Swift imzası isteğe bağlı bir görünüm kabul etse de Apple bu bağımsız değişkenin nil olmaması gerektiğini açıkça belirtir. AppKit kaydırma hızı

macOS 26 ve sonraki sürümlerde AppKit hareket tanıyıcısını SwiftUI hiyerarşisine entegre etmenin belgelenmiş yolu NSGestureRecognizerRepresentable’dır. SwiftUI tanıyıcının oluşturulmasını ve güncellenmesini yönetir, ayrıca koordinat dönüştürme desteği sağlar. Apple, tanıyıcının temelindeki görünümün SwiftUI görünümünden farklı geometriye sahip olabileceği konusunda uyarır; bu sınırı geçerken temsilcinin dönüştürücüsünü kullanın. AppKit hareket entegrasyonu

Kutucuk etkileşimi için sürükleme güncellemelerini yöneten tek bileşen SwiftUI hareketi olsun ve bırakma yayı mevcut ofsetini animasyonlasın. Bu aktarım çalıştığında, aynı durum ve koordinat sınırlarını koruyarak hedefi ve yay parametrelerini ayarlayın.

Kaynaklar