Swift: can't detect hit test on SCNNode? See if vector3 is contained in a node?

Viewed 7108

I am trying to just detect a tap on an SCNNode instantiated in my AR scene. It does not seem to work like SceneKit hit test results here, and I don't have much experience with scene kit.

I just want to detect if the tapped point is contained within any node in the scene, thus detecting a tap on the object. What I have tried from other answers:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }

        let results = sceneView.hitTest(touch.location(in: sceneView), types: [ARHitTestResult.ResultType.featurePoint])
        guard let hitFeature = results.last else { return }

        let hitTransform = SCNMatrix4.init(hitFeature.worldTransform)

        let hitPosition = SCNVector3Make(hitTransform.m41,
                                         hitTransform.m42,
                                         hitTransform.m43)

        if(theDude.node.boundingBoxContains(point: hitPosition))
        {

        }

    }

Nothing gets printed with the last if statement, which I got with:

extension SCNNode {
    func boundingBoxContains(point: SCNVector3, in node: SCNNode) -> Bool {
        let localPoint = self.convertPosition(point, from: node)
        return boundingBoxContains(point: localPoint)
    }

    func boundingBoxContains(point: SCNVector3) -> Bool {
        return BoundingBox(self.boundingBox).contains(point)
    }
}

struct BoundingBox {
    let min: SCNVector3
    let max: SCNVector3

    init(_ boundTuple: (min: SCNVector3, max: SCNVector3)) {
        min = boundTuple.min
        max = boundTuple.max
    }

    func contains(_ point: SCNVector3) -> Bool {
        let contains =
            min.x <= point.x &&
                min.y <= point.y &&
                min.z <= point.z &&

                max.x > point.x &&
                max.y > point.y &&
                max.z > point.z

        return contains
    }
}

And the ARHitTestResult doesn't contain nodes. What can I do to detect a tap on a node in an ARScene?

2 Answers

Add a UITapGestureRecognizer

let tapRec = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(rec:)))

Inside your handleTap method on state .ended:

@objc func handleTap(rec: UITapGestureRecognizer){
    if rec.state == .ended {
        let location: CGPoint = rec.location(in: sceneView)
        let hits = self.sceneView.hitTest(location, options: nil)
        if let tappednode = hits.first?.node {
            //do something with tapped object
        }
    }
}
Related