Reading input data from Manipulation Component

We can use chirality and kind to customize input based on the user’s hand or input device.

Overview

We’ve covered a lot on Manipulation Component. See our previous posts to learn how to get started.

Manipulation Component can surface some additional data such as chirality (left or right hand), kind (input type). We can read this during a Manipulation Event by checking the InputDevice. For example,

_ = content.subscribe(to: ManipulationEvents.WillBegin.self) { event in
  guard let inputDevice = event.inputDeviceSet.first else { return }
  
  let chirality = inputDevice.chirality
  let kind = inputDevice.kind
}

This can be useful when we need different behaviors for left vs. right hands. Or perhaps we want to apply components based on input type. For example, in Project Graveyard, I add a Billboard component when a user starts manipulating an entity. It looks great when using indirectPinch, but it seems kind of odd when using directPinch. I plan to update my app to check the kind and only add the Billboard when it is needed.

For this article, we’ll write the kind to either a left or right variable.

Example Code

struct Example104: View {

    @State private var leftKind = ""
    @State private var rightKind = ""

    var body: some View {
        RealityView { content in

            let subject = createStepDemoBox()

            // We'll use configureEntity to set up input and collision on the subject
            ManipulationComponent
                .configureEntity(
                    subject,
                    hoverEffect: .spotlight(.default),
                    collisionShapes: [.generateBox(width: 0.25, height: 0.25, depth: 0.25)]
                )

            _ = content.subscribe(to: ManipulationEvents.WillBegin.self) { event in
                guard let inputDevice = event.inputDeviceSet.first else { return }
                unpackChirality(inputDevice.chirality, value: unpackKind(inputDevice.kind)) // track the kind per hand/device
            }

            _ = content.subscribe(to: ManipulationEvents.DidHandOff.self) { event in
                guard let oldInputDeviceSet = event.oldInputDeviceSet.first else { return }
                unpackChirality(oldInputDeviceSet.chirality, value: "") // track the kind per hand/device
                guard let newInputDeviceSet = event.newInputDeviceSet.first else { return }
                unpackChirality(newInputDeviceSet.chirality, value: unpackKind(newInputDeviceSet.kind)) // track the kind per hand/device
            }

            _ = content.subscribe(to: ManipulationEvents.WillRelease.self) { event in
                guard let inputDevice = event.inputDeviceSet.first else { return }
                unpackChirality(inputDevice.chirality, value: "") // clear the correct value on release
            }

            content.add(subject)

        }
        .ornament(attachmentAnchor: .scene(.topLeading), ornament: {
            Text(leftKind)
                .frame(width: 160)
                .padding()
                .background(leftKind.isEmpty ? .clear : .green)
                .glassBackgroundEffect()
        })

        .ornament(attachmentAnchor: .scene(.topTrailing), ornament: {
            Text(rightKind)
                .frame(width: 160)
                .padding()
                .background(rightKind.isEmpty ? .clear : .green)
                .glassBackgroundEffect()
        })
    }

    func unpackChirality(_ chirality: ManipulationComponent.InputDevice.Chirality?, value: String) {
        switch chirality {
        case .left:
            leftKind = value
        case .right:
            rightKind = value
        case .none:
            return
        @unknown default:
            return
        }

    }

    func unpackKind(_ kind: ManipulationComponent.InputDevice.Kind) -> String {
        switch kind {
        case .pointer:
            return "pointer"
        case .directPinch:
            return "directPinch"
        case .indirectPinch:
            return "indirectPinch"
        default:
            return ""
        }
    }
}

Support our work so we can continue to bring you new examples and articles.

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?