Untuk shape yang diseret dalam app Mac anda, kemas kini ofsetnya secara langsung semasa gerak isyarat, kemudian animasikan ofset yang sama dengan spring apabila dilepaskan. Pada macOS 14 dan yang lebih baharu, SwiftUI boleh membawa halaju seretan yang dijejak ke dalam spring secara automatik.
Tutorial ini membina interaksi desktop kecil: seret jubin biru sintetik ke mana-mana arah, lepaskannya, dan biarkan spring mengembalikannya ke kedudukan asal. Semua keadaan kekal dalam memori, dan gerak isyarat itu dimiliki oleh jubin dalam tetingkap app itu sendiri.
Bagaimanakah spring mewarisi halaju akhir seretan?
Transaction.tracksVelocity SwiftUI merekodkan halaju apabila sifat boleh animasi berubah. Apple mendokumenkan bahawa panggilan balik gerak isyarat onChanged dan updating mendayakan penjejakan ini secara automatik. Apabila interaksi berakhir, animasi boleh menggunakan halaju yang terkumpul. Oleh itu, anda tidak perlu mengira dan menghantar halaju awal untuk interaksi SwiftUI biasa ini. Dokumentasi penjejakan halaju Apple menerangkan serahan ini dengan jelas.
Hubungan yang penting ialah sifat yang dianimasikan. Semasa menyeret, tulis translasi gerak isyarat ke dalam ofset jubin. Apabila dilepaskan, ubah ofset yang sama kepada destinasi di dalam withAnimation. Mengekalkan view dan keadaannya membolehkan SwiftUI menjejaki sifat secara berterusan.
Dalam pelaksanaan ini, pastikan kemas kini seretan tidak dianimasikan. Apple menjelaskan bahawa penjejakan halaju transaksi dan animasi yang mengurus halajunya sendiri tidak boleh digunakan serentak. Spring interaktif semasa menyeret ialah teknik lain yang disokong, tetapi teknik itu mengubah cara jubin mengikuti penuding; ia tidak diperlukan untuk interaksi ikut secara langsung di sini. Penjejakan halaju transaksi
Destinasi spring dan gerakan awalnya mempunyai tujuan yang berbeza. Destinasi menentukan tempat jubin patut berhenti. Halaju yang diwarisi pula menunjukkan cara jubin bergerak ketika dilepaskan. Jubin yang dilepaskan ketika bergerak menjauhi kedudukan asal boleh terus bergerak ke luar seketika sebelum spring membawanya kembali. Apple menunjukkan spring meneruskan gerakan berdasarkan halaju gerak isyarat dalam Animate with springs.
Bina contoh macOS lengkap untuk menyeret dan melepaskan
Cipta app macOS dalam Xcode menggunakan SwiftUI, tetapkan sasaran deployment kepada macOS 14.0 atau yang lebih baharu, kemudian gantikan fail titik masuk app yang dijana dengan kod berikut. Pastikan hanya ada satu pengisytiharan app @main dalam sasaran. Contoh ini menggunakan API pelengkapan animasi macOS 14. Dokumentasi pelengkapan animasi Apple
Ini ialah aplikasi lengkap dalam satu fail tanpa aset atau kebergantungan pakej. Tandatangan APInya disemak berdasarkan dokumentasi; aplikasi desktop ini tidak dikompil atau dijalankan dalam persekitaran penyelidikan ini.
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
}
}
}
Gerak isyarat dilekatkan pada jubin yang bergerak, manakala ruang koordinat bernama dimiliki oleh kanvas yang pegun. Ini menjadikan pilihan koordinat interaksi itu jelas. Dokumentasi DragGesture Apple menerangkan cara melekatkan seretan yang dikonfigurasi menggunakan pengubah suai gerak isyarat.
value.translation ialah jumlah anjakan sejak seretan bermula. Tetapkannya kepada offset; jangan tambahkannya berulang kali pada ofset sebelumnya. Penambahan berulang akan mengumpulkan gerakan yang sama berkali-kali. Dalam contoh ini, setiap seretan yang diterima bermula dari kedudukan asal, jadi ofset permulaan berasingan tidak diperlukan. Takrif translasi Apple
Pelepasan yang berjaya memanggil returnHome() serta-merta. Ia tidak menetapkan semula ofset terlebih dahulu atau menjadualkan spring untuk kemudian. Ini memastikan perubahan seretan yang dijejak dan animasi pelepasan kekal bersambung melalui sifat keadaan yang sama.
Mengapa asingkan keadaan seretan, kedudukan dan pemulangan?
Ketiga-tiga sifat ini menjawab soalan yang berbeza: offset menyimpan anjakan yang diminta, isDragging menjejak gerak isyarat yang sedang aktif, dan isReturning merekodkan fasa pemulangan contoh ini. Pemilik jubin menyimpan nilai ini secara setempat. Apple mengesyorkan supaya keadaan milik view diisytiharkan sebagai peribadi dan binding dikongsi hanya apabila view lain perlu mengubahnya. Dokumentasi State
Sintaks pembalut itu juga mempunyai tujuan khusus. Pembalut sifat mengurus akses kepada nilai yang dibalut dan boleh mendedahkan nilai terunjur menggunakan awalan tanda dolar. Di sini, $isDragging membekalkan unjuran keadaan gerak isyarat yang digunakan oleh updating; ia bukan bendera kedua yang diselenggara secara berasingan. The Swift Programming Language: Properties
Pembatalan perlu ditangani secara berasingan kerana onEnded hanya dijalankan apabila gerak isyarat selesai dengan jayanya. Apple mengesyorkan @GestureState untuk keadaan yang perlu ditetapkan semula tanpa mengira cara gerak isyarat berakhir. Contoh ini memerhatikan penetapan semula itu dan memulihkan jubin sintetik jika pemulangan biasa tidak sedang berlangsung. Cara sandaran ini mengembalikan jubin ke kedudukan asal secara serta-merta; ia tidak mendakwa mempunyai halaju pelepasan akhir bagi gerak isyarat yang dibatalkan. Tingkah laku pelengkapan gerak isyarat, GestureState
Contoh ini juga sengaja hanya menerima seretan baharu selepas animasi pemulangan selesai. Menetapkan ofset model kepada sifar memulakan animasi itu, jadi sifar sudah pun menjadi destinasi yang diminta walaupun jubin masih kelihatan bergerak. Jika sekatan ini dibuang begitu sahaja, anda perlu mereka bentuk cara seretan baharu mengambil alih daripada kedudukan yang sedang dipaparkan.
allowsHitTesting(!isReturning) mengawal sama ada jubin mengambil bahagian dalam pengesanan hit. Kriteria pelengkapan .removed melepaskan sekatan apabila keseluruhan animasi selesai. Ini mengelakkan tekaan tentang tempoh lengah berdasarkan parameter tempoh spring. Kawalan pengesanan hit, Pelengkapan penyingkiran animasi
Bagaimanakah anda patut melaras dan menyemak pelepasan?
Mulakan dengan destinasi tetap dan tetapan spring yang tetap. Ubah cara anda melepaskan jubin sebelum mengubah parameter animasi. Ini memisahkan pewarisan halaju daripada pelarasan spring.
Lakukan semakan berikut dalam tetingkap. Ini ialah urutan diagnosis yang disimpulkan daripada serahan yang didokumenkan, bukannya hasil pengukuran daripada contoh ini.
| Semakan | Tindakan | Perkara yang perlu diperhatikan |
|---|---|---|
| Pelepasan hampir pegun | Seret menjauhi kedudukan asal, berhenti seketika, kemudian lepaskan. | Pemulangan patut bermula dengan sedikit sahaja gerakan terbawa. |
| Pelepasan ke luar | Seret menjauhi kedudukan asal dan lepaskan ketika masih bergerak ke luar. | Spring patut membawa gerakan ke luar sebelum berpatah balik. |
| Pelepasan ke dalam | Seret menjauhi kedudukan asal, tukar arah menuju kedudukan asal, kemudian lepaskan. | Pemulangan patut meneruskan gerakan ke dalam. |
| Pelepasan pepenjuru | Lepaskan ketika bergerak pada kedua-dua paksi. | Pastikan serahan mengekalkan arah dan tidak berkelakuan seperti pemulangan mendatar sahaja. |
| Interaksi seterusnya | Tunggu sehingga pemulangan selesai, kemudian seret sekali lagi. | Seretan baharu patut bermula dari kedudukan asal. |
Laraskan duration untuk mengubah rentak dan bounce untuk mengubah sifat gerakan. 0.45 dan 0.2 dalam contoh ini ialah nilai permulaan ilustrasi, bukannya tetapan yang diwajibkan. Apple mentakrifkan tempoh sebagai tempoh persepsi dan menerangkan bahawa lantunan sifar ialah teredam kritikal, manakala nilai lantunan positif menghasilkan spring yang semakin melantun. Konfigurasi spring
Tempoh persepsi bukan janji tepat tentang bila setiap gerakan yang kelihatan akan berakhir. Apple membezakannya daripada tempoh penyelesaian, yang bergantung pada gerakan spring. Gunakan panggilan balik pelengkapan untuk fasa pemulangan contoh ini dan jangan anggap 0.45 sebagai pemasa. Animate with springs
Jika pelepasan kelihatan mengabaikan gerakan, periksa serahan terlebih dahulu: adakah gerak isyarat mengubah ofset yang sama seperti yang dianimasikan oleh spring? Adakah kod lain menulis ganti ofset itu sebelum pelepasan? Adakah view digantikan? Adakah pengubah suai animasi menganimasikan kemas kini seretan? Semakan ini berpunca terus daripada keperluan yang didokumenkan untuk menjejak sifat boleh animasi yang berubah sebelum menyerahkannya kepada animasi.
Bilakah anda patut membaca halaju secara jelas atau menggunakan AppKit?
DragGesture.Value.velocity mendedahkan halaju seretan semasa, dan onEnded membekalkan nilai akhir gerak isyarat selepas pelengkapan yang berjaya. Membacanya berguna apabila halaju mempengaruhi keputusan aplikasi, seperti memilih antara dua lokasi snap sintetik. Untuk destinasi pemulangan tetap dalam contoh ini, penjejakan automatik sudah menyediakan serahan kepada animasi. Halaju seretan, Nilai akhir gerak isyarat
Jika anda memilih API halaju awal spring yang eksplisit, semak perwakilan nilainya sebelum menghantar ukuran gerak isyarat. Demonstrasi animasi dipacu gerak isyarat Apple menukar halaju kepada halaju unit dengan membahagikannya dengan jarak perjalanan yang masih berbaki. Pengiraan ilustrasinya akan menukar 300 titik sesaat sepanjang 150 titik perjalanan kepada 2 jarak animasi sesaat. Penukaran itu juga memerlukan dasar yang jelas apabila jarak yang berbaki ialah sifar. Laluan SwiftUI automatik mengelakkan pengiraan ini di sini. Enhance your UI animations and transitions
Gunakan AppKit apabila view atau pengecam AppKit sedia ada yang dimiliki app perlu mengurus interaksi. NSPanGestureRecognizer.velocity(in:) mengembalikan halaju mendatar dan menegak dalam titik sesaat relatif kepada view yang dibekalkan. Walaupun tandatangan Swiftnya menerima view pilihan, Apple menyatakan dengan jelas bahawa argumen itu tidak boleh berupa nil. Halaju pan AppKit
Untuk macOS 26 dan yang lebih baharu, NSGestureRecognizerRepresentable ialah cara yang didokumenkan untuk menyepadukan pengecam gerak isyarat AppKit ke dalam hierarki SwiftUI. SwiftUI mengurus penciptaan dan pengemas kininya serta menyediakan sokongan penukaran koordinat. Apple memberi amaran bahawa geometri view asas pengecam mungkin berbeza daripada view SwiftUI, jadi gunakan penukar representable apabila merentasi sempadan itu. Penyepaduan gerak isyarat AppKit
Untuk interaksi jubin ini, kekalkan gerak isyarat SwiftUI sebagai satu-satunya pengurus kemas kini seretan dan biarkan spring pelepasan menganimasikan ofset sedia ada. Setelah serahan itu berfungsi, laraskan destinasi dan parameter spring sambil mengekalkan keadaan dan sempadan koordinat yang sama.
Sumber
tracksVelocity — Apple Developer
Animate with springs — Apple Developer
withAnimation(:completionCriteria::completion:) — Apple Developer
DragGesture — Apple Developer
translation — Apple Developer
State — Apple Developer
Properties — Swift.org
onEnded(_:) — Apple Developer
GestureState — Apple Developer
allowsHitTesting(_:) — Apple Developer
removed — Apple Developer
spring(duration:bounce:blendDuration:) — Apple Developer
velocity — Apple Developer
Enhance your UI animations and transitions — Apple Developer
velocity(in:) — Apple Developer
NSGestureRecognizerRepresentable — Apple Developer