Spatial Tap Gesture

Spatial Tap Gesture adds the location of the tap on the entity. We can use this to place indicators or spawn new models in our scene.

For most cases, we can use Tap Gesture to work with 3D content. As long as we use targetedToAnyEntity, Tap Gesture will return an entity along with its root and any children. We can use that data to move entities, run animations, etc. But what if we want to place an indicator at the location where we tapped?

SpatialTapGesture is a variant of TapGesture that adds data for the location of the tap. It provides both location (CGPoint) and location3D (Point3D). In this example I’ll use location3D.

The subject is a cube with input and collision components. It’s important to keep in mind that the tap takes place on the surface of the collision, not necessarily the surface of the mesh. It may be tricky to use this with complex meshes that don’t line up with their collision components.

struct Example005: View {

    @State var subject: Entity?
    @State var indicator: Entity?

    var body: some View {
        RealityView { content in
            // Load the scene from the bundle
            if let scene = try? await Entity(named: "SpatialTapLab", in: realityKitContentBundle) {
                content.add(scene)

                // We will place this indicator at the location of our taps
                let indicatorModel = ModelEntity(
                    mesh: .generateSphere(radius: 0.025),
                    materials: [SimpleMaterial(color: .black, isMetallic: false)])

                // Get the cube from the scene. This has Input and Collision components
                if let cube = scene.findEntity(named: "Cube") {
                    cube.components.set(HoverEffectComponent())
                    cube.addChild(indicatorModel)
                    subject = cube
                    indicator = indicatorModel
                }

            }

        }
        .gesture(spatialTapExample)
    }

    var spatialTapExample: some Gesture {
        SpatialTapGesture()
            .targetedToAnyEntity()
            .onEnded { value in

                if let subject = subject, let indicator = indicator {
                    // Convert the location3D value to the coordinate space of the subject
                    // Place the indicator on the surface of the subject
                    let tappedPosition = value.convert(value.location3D, from: .local, to: subject)
                    indicator.position = tappedPosition
                }

            }
    }
}

When we tap the subject in this lab, we convert the location3D value to the local coordinate space of the subject. Then we use that value to position the indicator.

Video Demo

Download the Xcode project with this and many more examples from Step Into Vision.

Some examples are provided as standalone Xcode projects. You can find those here.

Questions or feedback?