问题
I have placed a SCNNode (a plane) at the location of a recognized image in ARKit 1.5 beta. I would like to print a message to the console when the plane is tapped on. So far I have this code:
// MARK: - ARSCNViewDelegate (Image detection results)
/// - Tag: ARImageAnchor-Visualizing
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
guard let imageAnchor = anchor as? ARImageAnchor else { return }
let referenceImage = imageAnchor.referenceImage
updateQueue.async {
// Create a plane to visualize the initial position of the detected image.
let plane = SCNPlane(width: referenceImage.physicalSize.width,
height: referenceImage.physicalSize.height)
let planeNode = SCNNode(geometry: plane)
planeNode.opacity = 0.25
/*
`SCNPlane` is vertically oriented in its local coordinate space, but
`ARImageAnchor` assumes the image is horizontal in its local space, so
rotate the plane to match.
*/
planeNode.eulerAngles.x = -.pi / 2
/*
Image anchors are not tracked after initial detection, so create an
animation that limits the duration for which the plane visualization appears.
*/
//planeNode.runAction(self.imageHighlightAction)
// Add the plane visualization to the scene.
node.addChildNode(planeNode)
}
DispatchQueue.main.async {
let imageName = referenceImage.name ?? ""
self.statusViewController.cancelAllScheduledMessages()
self.statusViewController.showMessage("Detected image “\(imageName)”")
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first as! UITouch
if(touch.view == self.sceneView){
print("touch working")
let viewTouchLocation:CGPoint = touch.location(in: sceneView)
guard let result = sceneView.hitTest(viewTouchLocation, options: nil).first else {
return
}
if let planeNode = planeNode, planeNode == result.node {
print("match")
}
}
}
But I'm getting an 'unresolved identifier' error on this line: if let planeNode = planeNode, planeNode == result.node {
, which I understand because planeNode is defined in the Renderer function above and is not in the right scope. My question is how to fix this, because I dont believe I can return the value in Renderer, nor can I put the touchesBegan function in the Renderer function so its in the right scope. Can anyone give me any ideas on how to fix this? Thanks!
回答1:
To solve this problem declare a global variable in your ViewController
class ViewController: UIViewController, ARSCNViewDelegate {
@IBOutlet var sceneView: ARSCNView!
var planeNode : SCNNode? = nil
.....
}
Then use the planeNode variable just declared and assign it to your actual planeNode in the renderer method
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
.........
let planeNode = SCNNode(geometry: plane)
self.planeNode = planeNode
}
This way you can access the planeNode from anywhere inside the class
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first as! UITouch
if(touch.view == self.sceneView){
print("touch working")
let viewTouchLocation:CGPoint = touch.location(in: sceneView)
guard let result = sceneView.hitTest(viewTouchLocation, options: nil).first else {
return
}
if let planeNode = planeNode, planeNode == result.node {
print("match")
}
}
}
回答2:
// Add a gesture recognizer
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap))
self.sceneView.addGestureRecognizer(tapGestureRecognizer)
tapGestureRecognizer.cancelsTouchesInView = false
@objc func handleTap(sender:UITapGestureRecognizer) {
guard let sceneView = sender.view as? ARSCNView else {return}
let touchLcoation = sender.location(in: sceneView)
let hitTestResult = sceneView.hitTest(touchLcoation, types: .existingPlaneUsingExtent)
if !hitTestResult.isEmpty {
// add something to scene
// e.g self.sceneView.scene.rootNode.addChildNode(yourNode!)
}
}
来源:https://stackoverflow.com/questions/48854151/detect-touch-on-scnnode-in-arkit