Untuk seret dan lepas dalam apl Mac anda, sumber mengisytiharkan operasi yang disokong, destinasi memilih satu, dan model anda melaksanakan hasilnya. Dalam SwiftUI pada macOS 26 atau lebih baharu, konfigurasikan kedua-dua hujung dan buang yang asal hanya selepas pemindahan selesai.
Artikel ini membina interaksi desktop kecil: seret satu bulatan sintetik ke Salin di sini atau Pindah di sini. Semuanya kekal dalam memori. Contoh ini menjelaskan operasi, identiti item dan pembersihan sumber, kemudian memetakan tanggungjawab yang sama kepada AppKit.
Apakah yang perlu disepakati oleh sumber dan destinasi?
Muatan boleh pindah menerangkan perkara yang bergerak antara paparan. Salin atau pindah menerangkan perkara yang berlaku kepada yang asal. Ini ialah keputusan yang berasingan. Dokumentasi SwiftUI Apple menyatakan bahawa penyeretan menyokong penyalinan secara lalai; mengaktifkan pemindahan menambah satu lagi operasi yang disokong. Destinasi kemudian memilih cara menerima item itu. DragConfiguration
Gunakan tiga tanggungjawab untuk mereka bentuk interaksi:
| Tanggungjawab | Salin | Pindah |
|---|---|---|
| Keupayaan sumber | Tawarkan salinan | Tawarkan pemindahan |
| Pengesahan destinasi | Masukkan item bebas | Terima item sedia ada |
| Penyelesaian sumber | Kekalkan yang asal | Buang yang asal selepas pemindahan selesai |
Untuk contoh bulatan ini, salinan bebas mendapat pengecam baharu. Pemindahan mengekalkan pengecam sambil menukar lokasinya. Ini ialah pilihan reka bentuk aplikasi, selaras dengan keperluan Swift bahawa entiti yang boleh dikenal pasti mempunyai identiti yang stabil; Swift menyerahkan skop dan tempoh identiti itu kepada aplikasi. Identifiable
Kesannya dari segi praktikal ialah memulangkan operasi pemindahan tidak memindahkan elemen dalam tatasusunan anda. Anda masih perlu memasukkan item di destinasi dan membuangnya dari sumber. Apple secara khusus menunjukkan pemerhatian terhadap operasi seret yang selesai untuk memaklumkan model bila item sumber perlu dibuang. onDragSessionUpdated(_:)
API SwiftUI manakah yang patut digunakan oleh apl Mac?
Untuk pelaksanaan ini, gunakan sasaran penggunaan macOS 26 atau lebih baharu dan SDK yang mengandungi pengubah konfigurasi yang didokumenkan. Tambahkan draggable(_:) pada sumber, dragConfiguration(_:) untuk mengisytiharkan operasinya, dan dropConfiguration(_:) untuk memilih operasi destinasi. Kedua-dua pengubah konfigurasi didokumenkan sebagai tersedia pada macOS 26. dragConfiguration(_:), dropConfiguration(_:)
Lebihan beban dropDestination(for:isEnabled:action:) semasa memberikan DropSession kepada tindakannya. Tindakannya memulangkan Void. Ini berbeza daripada lebihan beban lama yang tindakannya menerima lokasi dan memulangkan nilai Boolean. Nyatakan isEnabled: secara eksplisit dalam contoh supaya lebihan beban yang dimaksudkan jelas. dropDestination(for:isEnabled:action:)
Di destinasi, periksa suggestedOperations sebelum memilih operasi. Untuk sasaran umum yang mengutamakan pemindahan, pilih pemindahan jika ditawarkan, jika tidak pilih salinan jika ditawarkan, dan jika tidak pilih dilarang. Contoh yang didokumenkan Apple menggunakan pilihan sandaran pemindahan-ke-salinan ini dan menyatakan bahawa penutupan konfigurasi dijalankan dengan kerap, jadi pastikan ia ringan. dropConfiguration(_:)
Kedua-dua sasaran kita sengaja mempunyai makna tetap. Salin di sini menerima salinan; Pindah di sini menerima pemindahan. Sasaran memulangkan operasi dilarang apabila operasi yang dinamakannya tidak tersedia. Ini menjadikan hasilnya boleh diramal tanpa memerlukan pengubah suai papan kekunci.
Bina contoh lengkap penyalinan dan pemindahan dalam memori
Cipta apl SwiftUI macOS, tetapkan sasaran penggunaannya kepada macOS 26 atau lebih baharu, dan gantikan pengisytiharan apl serta paparan kandungan yang dijana dengannya menggunakan fail tunggal berikut. Contoh ini memindahkan rentetan terbina dalam yang mengandungi pengecam item, jadi ia tidak memerlukan jenis boleh pindah tersuai atau pengisytiharan jenis tersuai. Apple mendokumenkan kedua-dua muatan boleh pindah untuk sumber seret dan muatan rentetan untuk destinasi lepas. draggable(_:), dropDestination(for:isEnabled:action:)
Ini ialah apl minimum lengkap yang direka untuk satu tetingkap terbuka dan satu item sumber. Saya menyemak rujukan API-nya berbanding dokumentasi yang diperoleh; saya tidak dapat mengompil atau menjalankannya di sini kerana Swift dan SDK macOS tidak tersedia.
import SwiftUI
import Foundation
@main
struct CopyMoveDemoApp: App {
var body: some Scene {
WindowGroup {
CopyMoveDemo()
}
}
}
struct CopyMoveDemo: View {
@State private var sourceID = UUID()
@State private var sourcePresent = true
@State private var copiedIDs: [UUID] = []
@State private var movedIDs: [UUID] = []
@State private var moveReceived = false
private var sourceConfiguration: DragConfiguration {
DragConfiguration(
operationsWithinApp: .init(allowMove: true),
operationsOutsideApp: .init(
allowCopy: false,
allowMove: false,
allowDelete: false
)
)
}
var body: some View {
VStack(spacing: 24) {
Text("Drag the circle to a target")
.font(.headline)
if sourcePresent {
Circle()
.fill(.blue)
.frame(width: 90, height: 90)
.overlay(Text("Circle").foregroundStyle(.white))
.draggable(sourceID.uuidString)
.dragConfiguration(sourceConfiguration)
.onDragSessionUpdated { session in
if session.phase == .initial {
moveReceived = false
}
if session.phase == .ended(.move), moveReceived {
sourcePresent = false
}
if case .ended = session.phase {
moveReceived = false
}
}
} else {
Text("Original moved")
.frame(width: 90, height: 90)
}
HStack(alignment: .top, spacing: 20) {
target(moving: false)
target(moving: true)
}
}
.padding(24)
.frame(minWidth: 520, minHeight: 320)
}
private func target(moving: Bool) -> some View {
let operation: DropOperation = moving ? .move : .copy
let ids = moving ? movedIDs : copiedIDs
return VStack(spacing: 10) {
Text(moving ? "Move here" : "Copy here")
.font(.headline)
ForEach(ids, id: \.self) { id in
Text("Circle \(id.uuidString.prefix(4))")
}
Spacer(minLength: 20)
}
.frame(width: 210, height: 150)
.background(.blue.opacity(0.08))
.contentShape(Rectangle())
.dropDestination(for: String.self, isEnabled: true) {
tokens, session in
guard session.localSession != nil,
sourcePresent,
tokens == [sourceID.uuidString] else {
return
}
if moving {
guard !movedIDs.contains(sourceID) else { return }
movedIDs.append(sourceID)
moveReceived = true
} else {
copiedIDs.append(UUID())
}
}
.dropConfiguration { session in
guard sourcePresent,
session.localSession != nil,
session.itemsCount == 1,
session.suggestedOperations.contains(operation) else {
return DropConfiguration(operation: .forbidden)
}
return DropConfiguration(operation: operation)
}
}
}
Konfigurasi sumber menawarkan penyalinan dan pemindahan dalam apl. Operasi di luar apl dinyahdayakan menggunakan pemula macOS untuk operasi di luar apl. Destinasi juga memeriksa localSession, yang didokumenkan Apple sebagai tiada bagi seretan yang bermula dari tempat lain. Tetapan ini memastikan contoh tertumpu pada item dalam memori di tetingkapnya sendiri. DragConfiguration.OperationsWithinApp, init(allowCopy:allowMove:allowDelete:), DropSession
Tindakan lepas mengesahkan token yang tepat sebelum memasukkan apa-apa. Penyalinan menambahkan pengecam baharu. Pemindahan menambahkan pengecam asal dan merekodkan bahawa destinasi ini telah menerimanya. Sumber kemudian membuang yang asal daripada paparan hanya apabila sesi berakhir dengan pemindahan dan rekod penerimaan itu wujud. Rekod penerimaan ini ialah perlindungan pada peringkat aplikasi untuk demonstrasi terhad ini, bukannya ciri transaksi SwiftUI.
Pengisytiharan fasa seret Apple semasa merangkumi initial, active, ending, ended dan dataTransferCompleted. Contoh ini menggunakan kes ended(DropOperation) yang didokumenkan untuk hasil operasi. Contoh ini tidak membuang sumber ketika penuding berlegar atau semasa fasa penamatan yang lebih awal. DragSession.Phase
Pastikan sempadan pemilikan dan gerak isyarat jelas
Rentetan itu ialah token carian, bukannya model bulatan yang lengkap. Destinasi menyelesaikan token itu berdasarkan keadaan sumber yang dimiliki oleh paparan ini. Untuk berbilang dokumen atau tetingkap, gantikan carian setempat itu dengan pemilik model yang jelas dan rekod penerimaan yang dikaitkan dengan sesi seret serta item sebenar. Seretan yang bermula dalam apl itu sendiri tidak menentukan dokumen yang memiliki item.
Untuk berbilang item yang boleh diseret, gunakan pengecam model yang stabil dan dapatkan pengecam yang diseret daripada sesi apabila tersedia. Apple menyediakan draggedItemIDs(for:) untuk muatan boleh dikenal pasti atau pengecam yang dibekalkan secara berasingan. Elakkan daripada membuang apa-apa yang sedang dipilih apabila penyelesaian tiba; pemilihan dan identiti item yang diseret ialah input yang berbeza. DragSession
Biarkan pengubah suai draggable(_:) SwiftUI memasang interaksi seret. Pastikan pembinaan muatan tidak membuang model. Apple mendokumenkan bahawa pengubah suai itu menambah gerak isyarat yang sesuai dan mengingatkan supaya penutupan pengeksport tidak dihantar ke pelaksana utama. draggable(_:)
Bagaimanakah kesepakatan yang sama berfungsi dalam AppKit?
Gunakan AppKit apabila sasaran penggunaan anda atau seni bina paparan sedia ada memerlukan kitaran hayat seret yang jelas. Protokol sumbernya mengisytiharkan operasi yang dibenarkan melalui draggingSession(_:sourceOperationMaskFor:). Konteksnya membolehkan anda membezakan destinasi dalam dan luar aplikasi. Untuk interaksi di atas, tawarkan penyalinan dan pemindahan dalam apl dan tiada operasi di luarnya. draggingSession(_:sourceOperationMaskFor:)
Destinasi mendaftarkan jenis papan tampal yang berkaitan, kemudian memilih operasi dalam draggingEntered(_:) dan mengemas kini pilihan itu jika perlu dalam draggingUpdated(_:). Apple menghendaki destinasi mempertimbangkan pertindihan antara operasi sumber dengan operasi dan jenis data yang disokongnya sendiri. Pulangkan satu operasi, atau tiada operasi jika tiada pertindihan yang boleh diterima. NSDraggingDestination, draggingEntered(_:)
Lakukan penyisipan dalam performDragOperation(_:), selepas persediaan berjaya. Pulangkan true hanya apabila destinasi menerima data itu. Sumber menerima operasi yang selesai melalui draggingSession(_:endedAt:operation:); gunakan hasil itu bersama peraturan pemilikan model anda untuk menyelesaikan pembuangan pada pihak sumber bagi pemindahan. performDragOperation(_:), draggingSession(_:endedAt:operation:)
Jika anda membenamkan permukaan seret AppKit dalam antara muka SwiftUI, biarkan permukaan itu mengendalikan interaksi AppKit. beginDraggingSession(with:event:source:) menerima peristiwa tekan tetikus yang memulakan interaksi, dan bingkai item seret menggunakan sistem koordinat paparan. Kekalkan hubungan peristiwa dan koordinat itu, bukannya mereka-reka peristiwa daripada nilai gerak isyarat SwiftUI yang diterima kemudian. beginDraggingSession(with:event:source:)
Kod destinasi SwiftUI yang lebih lama boleh memulangkan DropProposal(operation: .move) daripada DropDelegate.dropUpdated(info:). Itu menyatakan cadangan destinasi. Ia tidak menggantikan konfigurasi keupayaan sumber, pemerhatian operasi yang selesai atau mutasi model anda. Apabila API sumber SwiftUI yang lebih baharu tidak tersedia dan anda memerlukan kawalan jelas pada kedua-dua hujung, kitaran hayat sumber dan destinasi AppKit menyediakan laluan tersebut. dropUpdated(info:), NSDraggingSource
Sahkan operasi dan model bersama-sama
Untuk contoh ini, gunakan senarai semak keadaan yang dijangka berikut. Ini ialah syarat penerimaan yang diperoleh daripada pelaksanaan, bukannya keputusan ujian yang dilaporkan:
| Tindakan | Asal | Sasaran salinan | Sasaran pemindahan |
|---|---|---|---|
Lepas sekali pada Salin di sini |
Kekal | Mendapat satu pengecam baharu | Tidak berubah |
| Salin sekali lagi | Kekal | Mendapat satu lagi pengecam baharu | Tidak berubah |
Lepas pada Pindah di sini |
Hilang selepas pemindahan selesai | Tidak berubah | Menerima pengecam asal |
| Batal atau lepaskan jauh daripada kedua-dua sasaran | Kekal | Tidak berubah | Tidak berubah |
Apabila tingkah laku menyimpang, periksa peringkat mengikut turutan: keupayaan sumber, operasi destinasi, penyisipan destinasi dan penyelesaian sumber. Asal yang berganda selepas pemindahan menunjukkan kemungkinan pembersihan sumber yang tiada. Dua kali pembuangan menunjukkan kemungkinan pemilik model yang bersaing. Salinan yang tidak dijangka menunjukkan kemungkinan sumber tidak menawarkan pemindahan atau destinasi memilih salinan.
Pastikan tanggungjawab akhirnya jelas: operasi yang dirundingkan menerangkan pemindahan, manakala model anda melaksanakan lokasi dan identiti item. Tawarkan operasi yang anda sokong, pilih operasi yang dibenarkan di destinasi dan lakukan setiap perubahan model sekali pada sempadan kitaran hayat yang didokumenkan.
Sumber
DragConfiguration — Apple
Identifiable — Apple
onDragSessionUpdated(_:) — Apple
dragConfiguration(_:) — Apple
dropConfiguration(_:) — Apple
draggable(_:) — Apple
DropSession — Apple
DragSession.Phase — Apple
DragSession — Apple
NSDraggingDestination — Apple
draggingEntered(_:) — Apple
performDragOperation(_:) — Apple
dropUpdated(info:) — Apple
NSDraggingSource — Apple