För dra och släpp i din Mac-app deklarerar källan vilka åtgärder som stöds, destinationen väljer en av dem och din modell genomför resultatet. I SwiftUI på macOS 26 eller senare konfigurerar du båda ändarna och tar bort originalet först när en flytt har slutförts.
Den här artikeln bygger en liten skrivbordsinteraktion: dra en syntetisk cirkel till antingen Copy here eller Move here. Allt stannar i minnet. Exemplet gör åtgärden, objektets identitet och källans rensning tydliga och visar sedan hur samma ansvar fördelas i AppKit.
Vad måste källan och destinationen komma överens om?
En överförbar nyttolast beskriver vad som förs mellan vyer. Kopiering eller flytt beskriver vad som händer med originalet. Det är separata beslut. Apples SwiftUI-dokumentation säger att dra stöder kopiering som standard; om du aktiverar flytt tillkommer ytterligare en åtgärd som stöds. Destinationen väljer sedan hur objektet ska tas emot. DragConfiguration
Använd tre ansvarsområden när du utformar interaktionen:
| Ansvar | Kopiering | Flytt |
|---|---|---|
| Källans kapacitet | Erbjud kopiering | Erbjud flytt |
| Destinationens åtagande | Lägg in ett fristående objekt | Ta emot det befintliga objektet |
| Källans slutförande | Behåll originalet | Ta bort originalet när flytten har slutförts |
I det här cirkelexemplet får en fristående kopia ett nytt ID. En flytt bevarar ID:t men ändrar platsen. Det är ett designval för programmet som stämmer överens med Swifts krav på att en identifierbar entitet ska ha en stabil identitet; Swift överlåter identitetens omfattning och varaktighet till programmet. Identifiable
Den praktiska följden är att det inte räcker att returnera en flyttåtgärd för att flytta ett element mellan dina arrayer. Du måste fortfarande lägga in objektet på destinationen och ta bort det från källan. Apple visar särskilt hur du kan observera den slutförda dragåtgärden för att tala om för modellen när ett källobjekt ska tas bort. onDragSessionUpdated(_:)
Vilka SwiftUI-API:er bör en Mac-app använda?
För den här implementationen använder du ett distributionsmål för macOS 26 eller senare och ett SDK som innehåller de dokumenterade konfigurationsmodifierarna. Lägg till draggable(_:) i källan, dragConfiguration(_:) för att deklarera dess åtgärder och dropConfiguration(_:) för att välja destinationens åtgärd. Båda konfigurationsmodifierarna är dokumenterade som tillgängliga i macOS 26. dragConfiguration(_:), dropConfiguration(_:)
Den aktuella överlagringen av dropDestination(for:isEnabled:action:) skickar en DropSession till sin åtgärd. Åtgärden returnerar Void. Detta skiljer sig från den äldre överlagringen, vars åtgärd tar emot en plats och returnerar ett booleskt värde. Ange isEnabled: uttryckligen i exemplet så att det är tydligt vilken överlagring som avses. dropDestination(for:isEnabled:action:)
På destinationen granskar du suggestedOperations innan du väljer en åtgärd. För ett generellt mål som föredrar flytt väljer du flytt om den erbjuds, annars kopiering om den erbjuds, och annars förbjudet. Apples dokumenterade exempel använder denna reservlösning från flytt till kopiering och påpekar att konfigurationsstängningen körs ofta, så håll den billig att köra. dropConfiguration(_:)
Våra två mål har medvetet fasta betydelser. Copy here godtar kopiering; Move here godtar flytt. Ett mål returnerar förbjudet om den namngivna åtgärden inte är tillgänglig. Det gör resultatet förutsägbart utan att kräva en tangentbordsmodifierare.
Bygg ett komplett exempel på kopiering och flytt i minnet
Skapa en macOS SwiftUI-app, ange macOS 26 eller senare som distributionsmål och ersätt appens och innehållsvyns genererade deklarationer med följande enda fil. Exemplet överför en inbyggd sträng som innehåller ett objekt-ID, så ingen anpassad överförbar typ eller typdeklaration behövs. Apple dokumenterar både överförbara nyttolaster för dragkällor och strängnyttolaster för släppmål. draggable(_:), dropDestination(for:isEnabled:action:)
Det här är en komplett, minimal app avsedd för ett enda öppet fönster och ett enda källobjekt. Jag granskade API-referenserna mot den hämtade dokumentationen; jag kunde inte kompilera eller köra appen här eftersom Swift och macOS SDK saknas.
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)
}
}
}
Källkonfigurationen erbjuder kopiering och flytt i appen. Den inaktiverar åtgärder utanför appen med macOS-initieraren för åtgärder utanför appen. Destinationen kontrollerar även localSession, som enligt Apples dokumentation saknas för ett drag som startar någon annanstans. Dessa inställningar håller exemplet fokuserat på objektet i minnet i det egna fönstret. DragConfiguration.OperationsWithinApp, init(allowCopy:allowMove:allowDelete:), DropSession
Släppåtgärden validerar den exakta token innan något läggs in. Kopiering lägger till ett nytt ID. Flytt lägger till originalets ID och registrerar att destinationen tog emot det. Källan tar sedan bort sitt visade original först när sessionen avslutas med flytt och den registreringen finns. Registreringen är en programspecifik skyddsåtgärd för den här begränsade demonstrationen, inte en SwiftUI-transaktionsfunktion.
Apples aktuella deklaration av dragfaser omfattar initial, active, ending, ended och dataTransferCompleted. Exemplet använder det dokumenterade fallet ended(DropOperation) för åtgärdsresultatet. Källan tas inte bort medan du hovrar eller under den tidigare avslutningsfasen. DragSession.Phase
Håll ägarskap och gestgränser tydliga
Strängen är en uppslagningstoken, inte cirkelns fullständiga modell. Destinationen slår upp den mot källtillståndet som den här vyn äger. För flera dokument eller fönster ersätter du den lokala uppslagningen med en uttrycklig modellägare och en registrering knuten till den faktiska dragsessionen och dess objekt. Att ett drag startar i appen fastställer inte i sig vilket dokument som äger ett objekt.
För flera dragbara objekt använder du stabila modell-ID:n och hämtar de dragna ID:na från sessionen när det är möjligt. Apple tillhandahåller draggedItemIDs(for:) för identifierbara nyttolaster eller separat angivna ID:n. Undvik att ta bort det som råkar vara markerat när slutförandet inträffar; markering och identiteten hos det dragna objektet är olika indata. DragSession
Låt SwiftUIs modifierare draggable(_:) installera draginteraktionen. Se till att konstruktionen av nyttolasten inte tar bort något från modellen. Apple dokumenterar att modifieraren lägger till lämpliga gester och varnar för att skicka en exporterande stängning till huvudaktören. draggable(_:)
Hur fungerar samma överenskommelse i AppKit?
Använd AppKit när distributionsmålet eller den befintliga vyarkitekturen kräver dess uttryckliga draglivscykel. Källprotokollet deklarerar tillåtna åtgärder via draggingSession(_:sourceOperationMaskFor:). Kontexten gör att du kan skilja mellan destinationer i och utanför programmet. För interaktionen ovan erbjuder du kopiering och flytt inom appen och ingen åtgärd utanför den. draggingSession(_:sourceOperationMaskFor:)
Destinationen registrerar sig för relevant pasteboard-typ, väljer sedan en åtgärd i draggingEntered(_:) och uppdaterar valet vid behov i draggingUpdated(_:). Apple kräver att destinationen tar hänsyn till överlappningen mellan källans åtgärder och dess egna åtgärder och datatyper som stöds. Returnera en åtgärd, eller ingen om det inte finns någon godtagbar överlappning. NSDraggingDestination, draggingEntered(_:)
Genomför insättningen i performDragOperation(_:) efter att förberedelserna har lyckats. Returnera true endast när destinationen godtar data. Källan får den slutförda åtgärden via draggingSession(_:endedAt:operation:); använd resultatet tillsammans med modellens ägarskapsregler för att slutföra borttagningen från källan vid en flytt. performDragOperation(_:), draggingSession(_:endedAt:operation:)
Om du bäddar in en AppKit-dragyta i ett SwiftUI-gränssnitt låter du den ytan äga AppKit-interaktionen. beginDraggingSession(with:event:source:) tar emot den initierande musnedtryckningshändelsen, och ramar för dragobjekt använder vyns koordinatsystem. Bevara sambandet mellan händelsen och koordinaterna i stället för att skapa en fabricerad händelse från ett senare SwiftUI-gestvärde. beginDraggingSession(with:event:source:)
Äldre SwiftUI-destinationskod kan returnera DropProposal(operation: .move) från DropDelegate.dropUpdated(info:). Det uttrycker destinationens förslag. Det ersätter inte konfiguration av källans kapacitet, observation av den slutförda åtgärden eller modelländringar. När de nyare SwiftUI-käll-API:erna inte är tillgängliga och du behöver uttrycklig kontroll över båda ändarna ger AppKits käll- och destinationslivscykel den möjligheten. dropUpdated(info:), NSDraggingSource
Kontrollera åtgärden och modellen tillsammans
Använd den här checklistan över förväntat tillstånd för exemplet. Det här är acceptanskriterier som härletts från implementationen, inte rapporterade testresultat:
| Åtgärd | Original | Kopieringsmål | Flyttmål |
|---|---|---|---|
Släpp en gång på Copy here |
Finns kvar | Får ett nytt ID | Oförändrat |
| Kopiera igen | Finns kvar | Får ytterligare ett nytt ID | Oförändrat |
Släpp på Move here |
Försvinner efter slutförd flytt | Oförändrat | Tar emot originalets ID |
| Avbryt eller släpp utanför båda målen | Finns kvar | Oförändrat | Oförändrat |
När beteendet avviker granskar du stegen i ordning: källans kapacitet, destinationens åtgärd, insättning på destinationen och källans slutförande. Ett duplicerat original efter en flytt tyder på att rensning av källan saknas. Två borttagningar tyder på konkurrerande modellägare. En oväntad kopia tyder på en källa som inte erbjöd flytt eller en destination som valde kopiering.
Håll det slutliga ansvaret tydligt: den förhandlade åtgärden beskriver överföringen, medan din modell hanterar objektets plats och identitet. Erbjud åtgärderna du stöder, välj en tillåten åtgärd på destinationen och gör varje modelländring en gång vid den dokumenterade livscykelgränsen.
Källor
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