Thanks for being a part of WWDC25!

How did we do? We’d love to know your thoughts on this year’s conference. Take the survey here

Summon gesture

Can you help to write a code able to pick an element a bit far from me, then bring it near to me, flick it a bit and then send it back to its original position when I release it?

Thanks a lot, Christophe

Hi @cuo001

There are many ways to do this. Here's a snippet to get you started. It uses SpatialEventGesture and a hand AnchorEntity. When a person begins the pinch gesture, the code appends the entity to an anchor entity attached to the index finger, mantaining its world position, then animates the entity to a position near the index finger. When the pinch ends, the code appends the entity to its previous parent and animates the entity to its previous position.

Create a custom component to store the state related to the custom interaction.

struct SummonComponent: Component {
    let leftAnchor: AnchorEntity
    let rightAnchor: AnchorEntity
    
    var isHolding = false
    var parentEntity: Entity?
    var previousTransform: Transform?
}

Implement the interaction.

struct ImmersiveView: View {
    @State var spatialTrackingSession = SpatialTrackingSession()
    
    var body: some View {
        RealityView { content in
            
            // Create the entity to summon.
            let box = ModelEntity(mesh: .generateBox(size: 0.15), materials: [SimpleMaterial(color: .red, isMetallic: true)])
            box.name = "Box"
            box.generateCollisionShapes(recursive: false)
            box.components.set(InputTargetComponent())
            box.position = [0, 1.2, -1]
            content.add(box)
            
            // Create entities to track a person's index fingers.
            let rightFingerTip = AnchorEntity(.hand(.right, location: .indexFingerTip))
            content.add(rightFingerTip)
            
            let leftFingerTip = AnchorEntity(.hand(.left, location: .indexFingerTip))
            content.add(leftFingerTip)
            
            box.components.set(SummonComponent(leftAnchor: leftFingerTip, rightAnchor: rightFingerTip))
        }
        .task {
            // Start a spatial tracking session otherwise `preservingWorldTransform` won't work.
            guard HandTrackingProvider.isSupported else { return }
            
            let configuration = SpatialTrackingSession.Configuration(
                tracking: [.hand])
            await spatialTrackingSession.run(configuration)
        }
        .gesture(
            SpatialEventGesture()
                .onChanged { events in
                    for event in events {
                        // When a person closes their fingers to pinch...
                        if event.kind == .indirectPinch,
                           let entity = event.targetedEntity,
                           var summonComponent = entity.components[SummonComponent.self],
                           summonComponent.isHolding == false {
                            // store the entity's parent and current transform. You'll use this information to return the entity when the person opens their fingers.
                            summonComponent.parentEntity = entity.parent
                            summonComponent.previousTransform = entity.transform
                            summonComponent.isHolding = true
                            
                            // Find the anchor entity attached to the person's index finger.
                            let anchor = event.chirality == .right ? summonComponent.rightAnchor : summonComponent.leftAnchor
                            
                            // Append the entity to the finger anchor.
                            // Preserve the world transform so the entity does not move.
                            anchor.addChild(entity, preservingWorldTransform: true)
                            
                            // Calculate an offset for the entity so it's slightly in front of the index finger.
                            let xOffset:Float = event.chirality == .right ? -0.08 : 0.08
                            
                            // Define a target transform for the entity that positions it near a person's finger and scales it.
                            let targetTransform = Transform(scale: .init(repeating: 0.5), translation: [xOffset, 0, 0])
                            
                            // Animate the entity to the new transform.
                            entity.move(to: targetTransform, relativeTo: anchor, duration: 0.3)
                            
                            entity.components.set(summonComponent)
                        }
                        
                    }
                }
                .onEnded { events in
                    for event in events {
                        // When the person opens their fingers...
                        if event.kind == .indirectPinch,
                           let entity = event.targetedEntity,
                           var summonComponent = entity.components[SummonComponent.self],
                           summonComponent.isHolding == true,
                           let parentEntity = summonComponent.parentEntity,
                           let previousTransform = summonComponent.previousTransform {
                            
                            // append the entity to its previous parent.
                            parentEntity.addChild(entity, preservingWorldTransform: true)
                            
                            // Animate it to its previous transform.
                            entity.move(to: previousTransform, relativeTo: parentEntity, duration: 0.3)
                            summonComponent.isHolding = false
                            summonComponent.previousTransform = nil
                            summonComponent.parentEntity = nil
                            
                            entity.components.set(summonComponent)
                        }
                    }
                }
        )
    }
}
Summon gesture
 
 
Q