Välj SwiftUI Canvas när du behöver kunna implementera uttryckliga placeringsregler för dragningen själv. Välj SpriteKit när former ska kunna knuffas, studsa eller fortsätta röra sig med hjälp av fysik. Börja med att bestämma vad som ska hända när en dragen form möter en annan form.
Den här guiden behandlar en uppgift: att välja interaktionsarkitektur för en Mac-app med syntetiska cirklar eller rektanglar i minnet som användarna drar inom en arbetsyta som ägs av appen. Beslutet beror på kollisionsbeteendet, hur indata hanteras och vilken komponent som styr varje forms position.
Vad ska hända när två former möts?
”Kollisionsrespons” kan syfta på flera olika krav. Att markera en överlappning, neka en ogiltig position och få två objekt att studsa kräver olika mekanismer. Apple skiljer uttryckligen mellan kontakter i SpriteKit, som rapporterar när kroppar vidrör varandra, och kollisioner, som beräknar en fysisk reaktion och tillämpar impulser. Om kollisioner och kontakter
| Önskat beteende | Praktisk utgångspunkt | Ansvar du behåller |
|---|---|---|
| Markera en överlappning medan pekaren placerar en form fritt | Canvas med egna geometriska tester | Träfftestning, överlappningsdetektering och visuell återkoppling |
| Stoppa en form vid det första hindret | Canvas med egna rörelsebegränsningar | Testa rörelsebanan och fastställa den godkända positionen |
| Knuffa närliggande former och låta dem stanna eller studsa | SpriteKit | Fysikkonfiguration och en dragregulator |
| Ge varje objekt vanliga SwiftUI-reglage och gester | Enskilda SwiftUI-vyer | Programspecifika regler för överlappning eller rörelse |
Det här är arkitekturrekommendationer som bygger på dokumenterade funktioner, inte prestandagränser från Apple. Ett litet antal former innebär inte automatiskt att Canvas är bäst, och ett stort antal innebär inte automatiskt att SpriteKit krävs. Välj först beteendet och mät sedan implementationen med representativa objekt.
Det finns också en viktig begränsning för interaktionen: exakt följning av pekaren och ett strikt förbud mot överlappning kan stå i konflikt med varandra. Om pekaren rör sig genom en upptagen position måste appen låta objekten överlappa, stoppa objektet bakom pekaren eller flytta andra objekt. Bestäm det innan du väljer renderare.
Vad erbjuder Canvas, och vad behöver du bygga själv?
Canvas erbjuder direkt renderad grafik via en grafikkontext. Cirklar, banor och symboler blir inte självständigt interaktiva element. Apple säger också att Canvas inte erbjuder hjälpmedelsstöd för enskilda ritade element, inte heller symboler som tillhandahålls som vyer. En gest som kopplas till canvasen behöver därför programlogik för att avgöra vilket objekt användaren har valt. Canvas
För en redigerare med uttryckliga placeringsregler sparar du formerna i en modell och låter ritningen återspegla modellen. Varje objekt kan innehålla en identifierare, mittpunkt, dimensioner och ritordning. Swifts riktlinjer rekommenderar strukturer som standard för vanliga data och klasser när delad identitet eller arv från ramverk behövs. En värdetypbaserad objektmodell och en SpriteKit-scenunderklass har olika funktioner. Välja mellan strukturer och klasser
En Canvas-dragning kan följa den här sekvensen:
- När gesten börjar testar du pekarens startposition mot objekten i omvänd ritordning och väljer det första som matchar.
- Behåll den valda identifieraren och dess ursprungliga mittpunkt under hela gesten.
- Beräkna varje föreslagen mittpunkt utifrån den ursprungliga mittpunkten plus gestens totala förflyttning.
- Tillämpa arbetsytans gränser och kollisionsregler för att ta fram en godkänd mittpunkt.
- Uppdatera modellen och låt Canvas rita om den.
- Rensa markeringen när gesten avslutas och definiera hur avbrutna interaktioner ska hanteras.
Skillnaden mellan total och stegvis förflyttning är viktig. SwiftUI definierar translation som förflyttningen från gestens start. Om du upprepade gånger lägger till den totala förflyttningen till en redan uppdaterad mittpunkt blir rörelsen för stor. Använd den sparade startmittpunkten varje gång. SwiftUI ger åtkomst till startLocation, location och translation via gestvärdet och låter dig ange gestens koordinatsystem. DragGesture.Value, DragGesture
Behåll markeringen oförändrad under dragningen. Om du upprepar den första träfftestningen vid varje pekaruppdatering kan det valda objektet bytas när pekaren passerar över en annan form. Om du bevarar den ursprungliga förskjutningen mellan pekaren och mittpunkten slipper du också att formen hoppar så att dess mittpunkt plötsligt hamnar under pekaren. Det här är regler för programdesign, inte beteenden som Canvas erbjuder automatiskt.
Genomräknat exempel: överlappningsdetektering jämfört med blockerad rörelse
Anta att två exempelcirklar har radien 20 punkter vardera. Cirkel A börjar vid (100, 100), och den stillastående cirkel B ligger vid (200, 100). Användaren vill flytta A:s mittpunkt till (190, 100). Avståndet mellan mittpunkterna skulle då vara 10 punkter, medan summan av radierna är 40. Den föreslagna positionen innebär en överlappning.
För att visa en överlappningsindikator godkänner du positionen och ändrar ritningen. Vid en blockerad horisontell dragning är den första beröringspositionen (160, 100): mittpunkterna ligger då 40 punkter från varandra. Beräkningen förutsätter att B står stilla, att båda cirklarna behåller sina storlekar och att A följer den horisontella banan.
Anta nu att en pekaruppdatering begär (260, 100). Om du bara testar slutpunkten hittar du ingen överlappning, trots att rörelsebanan passerar genom B. En implementation som blockerar dragningen behöver ett svept skärningstest eller en annan uttrycklig metod för att kontrollera den mellanliggande banan. Att bara avvisa överlappande slutpunkter innebär inte att objektet ”stannar vid det första hindret”.
Det här exemplet är det främsta skälet till att definiera kollisionsresponsen exakt. Några geometriska tester av slutpunkten kan räcka för att implementera en funktion som markerar överlappningar. Att stödja glidning runt hinder, samtidiga kontakter eller rörliga grannar gör den egna lösaren betydligt mer omfattande. Canvas är fortfarande ett alternativ för ritningen, men då ansvarar appen för det extra beteendet.
När passar SpriteKit bättre?
SpriteKit blir attraktivt när kollisionsresponsen ska uppstå genom en simulering. Om du kopplar en SKPhysicsBody till en nod kan beräkningar som involverar gravitation, friktion och kollisioner aktiveras; simuleringen uppdaterar sedan nodernas positioner och orienteringar. Den stöder volymkroppar för objekt och kantkroppar för gränser. SKPhysicsBody
Börja med två åtskilda cirklar och en rektangulär arbetsytegräns i den första prototypen. Ge cirklarna matchande cirkulära kroppar, inaktivera gravitationen för en arbetsyta som fungerar som en bordsskiva och avgör om rotation tillför något till interaktionen. Ställ in friktion, restitution och dämpning medvetet i stället för att förlita dig på standardvärdena. De egenskaperna styr olika aspekter av det fysiska beteendet. SKPhysicsBody
SwiftUI kan visa scenen direkt med SpriteView, som Apple beskriver som en SwiftUI-vy som renderar en SpriteKit-scen. Den dokumenterade tillgängligheten för Mac börjar med macOS 11; Canvas börjar med macOS 12. Därför behöver distributionsmålen vägas in i beslutet, även om interaktionsbeteendet fortfarande är det viktigaste kriteriet. SpriteView, Canvas
Konfigurera kollisionsrespons separat från aviseringar
Tilldela kategorier till dina former och gränser, och konfigurera sedan de två interaktionsmaskerna uttryckligen:
categoryBitMaskanger vilka kategorier en kropp tillhör.collisionBitMaskväljer de kategorier som fysiskt kan påverka kroppen.contactTestBitMaskbegär kontaktaviseringar för valda kategorier.
Kollisionsmasker utvärderas separat för varje kropp. För vanlig ömsesidig respons mellan två dynamiska former konfigurerar du båda kropparna så att de reagerar på den andras kategori. Kontaktaviseringar följer en annan regel: en matchande jämförelse mellan kategori och kontaktmask i endera riktningen kan generera en avisering. Att bara aktivera aviseringar konfigurerar inte fysisk blockering. collisionBitMask, contactTestBitMask
Om kropparna reagerar fysiskt men inget återanrop kommer fram bör du kontrollera kontaktmasken och fysikvärldens kontaktdelegerare. Om återanrop kommer fram medan formerna passerar genom varandra bör du granska kollisionsmaskerna och rörelseregulatorn. De här kontrollerna skiljer aviseringskonfigurationen från själva rörelseproblemet.
Vem styr positionen medan användaren drar?
En SpriteKit-dragning behöver en uttrycklig regulator. Fysikmotorn följer inte pekaren på egen hand. Välj mellan direkt placering och simuleringsstyrd rörelse utifrån beteendet du fastställde tidigare.
Vid direkt placering tilldelar programkoden den dragna nodens position. Det passar när pekaren måste kunna placera objektet exakt, men det hanterar inte i sig en fysiskt löst bana mellan två på varandra följande positioner. Om du stänger av isDynamic ignorerar kroppen krafter och impulser; det skapar ingen draglösare. Om du ändrar den här egenskapen tillfälligt bör du ange när den återställs och om rörelsen ska behållas eller nollställas när användaren släpper objektet. isDynamic
Vid simuleringsstyrd dragning sparar du pekarpositionen som ett mål och använder krafter eller en fjäderbaserad regulator för att styra en dynamisk kropp mot den. Simuleringen kan då lösa kontakter medan kroppen rör sig. Kompromissen är en avsiktlig fördröjning eller förflyttning från pekaren när ett annat objekt blockerar rörelsen. SpriteKit stöder krafter, och SKPhysicsJointSpring kopplar ihop två fysikkroppar med ett fjäderbeteende. Det här är byggstenar som kräver programspecifik justering. SKPhysicsBody, SKPhysicsJointSpring
Undvik att kontinuerligt tilldela pekarpositioner samtidigt som du förväntar dig att fysiken själv ska styra samma position. SpriteKits dokumenterade bildruteflöde kör appuppdateringar, åtgärder, fysik och efterföljande återanrop i separata steg. En användbar regel för ansvarsfördelning är att indata uppdaterar dragmålet, scenlogiken tillämpar regulatorn och fysiken beräknar kroppens resulterande position. Hantera händelser i bildrutans cykel
Kontaktåteranrop har också en dokumenterad begränsning. Apple säger att de körs under fysiksimuleringssteget, då det inte går att ändra fysikvärlden och ändringar av simulerade kroppar har odefinierat beteende. Registrera väntande arbete i återanropet och tillämpa fysikändringarna vid nästa uppdatering, enligt Apples mönster med flagga och uppdatering. SKPhysicsContactDelegate
Hur ska indata i macOS passera mellan ramverken?
Välj en enda komponent som hanterar indata för arbetsytan. En Canvas-implementation kan koppla en SwiftUI-gest av typen DragGesture till interaktionsytan. En egen AppKit-yta kan hantera händelser via responderkedjan; mouseDragged(with:) tar emot rörelse när vänster musknapp hålls nedtryckt. Låt inte en överlagrad SwiftUI-gest och en AppKit-hanterare var för sig flytta samma valda objekt. DragGesture, mouseDragged(with:)
Vid en brygga mellan AppKit och SpriteKit konverterar du koordinater vid varje gräns. NSEvent.locationInWindow uttrycks vanligtvis i det tillhörande fönstrets grundkoordinatsystem. Konvertera punkten till värdvyns koordinater och sedan från vykoordinater till scenkoordinater. Apple tillhandahåller convertPoint(fromView:) för det senare steget och kräver att scenen har presenterats innan metoden anropas. En nods position uttrycks i dess förälders koordinatsystem, så nästlade noder kan kräva ytterligare en konvertering. locationInWindow, convertPoint(fromView:), position
Använd SpriteView för enkel visning i SwiftUI. Om du behöver en egen AppKit-vy erbjuder NSViewRepresentable skapande, uppdateringar, samordning och nedmontering. Låt SwiftUI ansvara för layouten av den representerade vyn: Apple varnar uttryckligen för att själv tilldela den hanterade vyns ram eller gränser. NSViewRepresentable
Behåll scenen i en stabil ägare i stället för att skapa en ny scen varje gång SwiftUI räknar om vyinnehållet. Behåll objektmodellen på samma sätt för Canvas. Swifts dokumentation om modeller förklarar hur hanterat tillstånd bevarar en instans när en vy återskapas; dess Observation-metod kräver macOS 14 eller senare. Hantera scenkommandon och ögonblicksbilder av modellen som avsiktliga överföringar så att två ändringsbara kopior inte konkurrerar om att styra positionerna. Hantera modelldata i din app
Vad bör du testa innan du bestämmer dig?
Använd en liten syntetisk arbetsyta för att testa de svåra interaktionerna innan du lägger till visuell finputsning:
- Ta tag nära formens kant och bekräfta att den ursprungliga förskjutningen bevaras.
- Dra långsamt in i ett annat objekt och upprepa sedan med en stor pekarrörelse tvärs igenom det.
- Dra mot ett hörn med två närliggande former.
- Släpp medan objektet vidrör en annan form och kontrollera det avsedda beteendet när det stannar.
- Ändra arbetsytans storlek och verifiera träfftestning, gränser och koordinatkonvertering.
- Återskapa det omgivande SwiftUI-innehållet och kontrollera att objektens positioner finns kvar.
- Avbryt en dragning och kontrollera att markeringen och eventuella tillfälliga fysikinställningar återställs.
I SpriteKit kan du aktivera SKView.showsPhysics för att kontrollera att kropparnas geometri motsvarar den avsedda kollisionsytan. För små kroppar som rör sig snabbt kan du utvärdera usesPreciseCollisionDetection; Apple dokumenterar den högre beräkningskostnaden och funktionen att upptäcka kollisioner som vanliga bildrutebaserade kontroller kan missa. Den ersätter inte en genomtänkt dragregulator. SKView, usesPreciseCollisionDetection
Välj Canvas när placeringsreglerna är tydliga, avgränsade och enklare att uttrycka direkt. Välj SpriteKit när fysisk interaktion mellan former ingår i uppgiften. I båda lösningarna börjar en tillförlitlig dragning på datorn med ett enda valt objekt, uttryckliga koordinatkonverteringar och en komponent som ansvarar för varje positionsuppdatering.
Källor
- Canvas — Apple
- DragGesture — Apple
- DragGesture.Value — Apple
- Välja mellan strukturer och klasser — Apple
- Om kollisioner och kontakter — Apple
- SKPhysicsBody — Apple
- SpriteView — Apple
- collisionBitMask — Apple
- contactTestBitMask — Apple
- isDynamic — Apple
- SKPhysicsJointSpring — Apple
- Hantera händelser i bildrutans cykel — Apple
- SKPhysicsContactDelegate — Apple
- mouseDragged(with:) — Apple
- locationInWindow — Apple
- convertPoint(fromView:) — Apple
- position — Apple
- NSViewRepresentable — Apple
- Hantera modelldata i din app — Apple
- SKView — Apple
- usesPreciseCollisionDetection — Apple