Swift向SCNNode添加按钮
我在玩ARKit和图像检测。 现在我有了一个应用程序,可以检测图像,并将飞机放在屏幕上检测到的物体所在的位置 如何在平面上添加按钮之类的可单击元素。我希望在每个检测到的对象上都有一个单击事件 这是我的渲染器函数的外观:Swift向SCNNode添加按钮,swift,xcode,scenekit,arkit,xcode9-beta,Swift,Xcode,Scenekit,Arkit,Xcode9 Beta,我在玩ARKit和图像检测。 现在我有了一个应用程序,可以检测图像,并将飞机放在屏幕上检测到的物体所在的位置 如何在平面上添加按钮之类的可单击元素。我希望在每个检测到的对象上都有一个单击事件 这是我的渲染器函数的外观: func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { guard let imageAnchor = anchor as? ARImage
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
guard let imageAnchor = anchor as? ARImageAnchor else { return }
let referenceImage = imageAnchor.referenceImage
updateQueue.async {
let plane = SCNPlane(width: referenceImage.physicalSize.width,
height: referenceImage.physicalSize.height)
let planeNode = SCNNode(geometry: plane)
planeNode.opacity = 0.25
planeNode.eulerAngles.x = -.pi / 2
planeNode.runAction(self.imageHighlightAction)
node.addChildNode(planeNode)
}
DispatchQueue.main.async {
let imageName = referenceImage.name ?? ""
self.statusViewController.cancelAllScheduledMessages()
// self.statusViewController.showMessage("Detected image “\(imageName)”")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let second = storyboard.instantiateViewController(withIdentifier: "InfoViewController")as! InfoViewController
second.myStringValue = imageName
// self.navigationController?.pushViewController(second, animated: true)
}
}
有几种方法可以实现这一点: 1:标准方法: 在您的
delegate
方法中,为每个节点指定一个名称,如下所示(显然,如果您有许多节点,您希望根据需要将它们存储在数组或字典中):
然后使用触摸开始
,执行hitTest并相应地处理您的触摸:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
/*
1. Get The Current Touch Location
2. Check That We Have Touched A Valid Node
3. Check If The Node Has A Name
4. Handle The Touch
*/
guard let touchLocation = touches.first?.location(in: augmentedRealityView),
let hitNode = augmentedRealityView?.hitTest(touchLocation, options: nil).first?.node,
let nodeName = hitNode.name
else {
//No Node Has Been Tapped
return
}
//Handle Event Here e.g. PerformSegue
print(nodeName)
}
在委托方法中,执行以下操作:
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
//1. Check We Have The Image Anchor
guard let imageAnchor = anchor as? ARImageAnchor else { return }
//2. Get The Reference Image
let referenceImage = imageAnchor.referenceImage
//1. Create The Plane Geometry With Our Width & Height Parameters
let planeGeometry = SCNPlane(width: referenceImage.physicalSize.width,
height: referenceImage.physicalSize.height)
//2. Create A New Material
let material = SCNMaterial()
DispatchQueue.main.async {
//3. Create The New Clickable View
let clickableElement = ClickableView(frame: CGRect(x: 0, y: 0,
width: 300,
height: 300))
clickableElement.tag = 1
//4. Add The Clickable View As A Materil
material.diffuse.contents = clickableElement
}
//5. Create The Plane Node
let planeNode = SCNNode(geometry: planeGeometry)
planeNode.geometry?.firstMaterial = material
planeNode.opacity = 0.25
planeNode.eulerAngles.x = -.pi / 2
//6. Add It To The Scene
node.addChildNode(planeNode)
}
这会让你开始…谢谢!这对我帮助很大。第二种方法非常好。对于任何试图实现此功能并希望通过“显示/推送”导航到其他屏幕的人,请注意,SceneKit会为分配给材质的每个此类UIKit元素创建新的
UIWindow
对象,这就是它发送要处理的事件的方式。因此,请注意进行必要的清理以避免不必要的行为(例如,您可以迭代UIApplication.shared.windows
并隐藏非关键的窗口)。
/// Clickable View
class ClickableView: UIButton{
override init(frame: CGRect) {
super.init(frame: frame)
self.addTarget(self, action: #selector(objectTapped(_:)), for: .touchUpInside)
self.backgroundColor = .red
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
/// Detects Which Object Was Tapped
///
/// - Parameter sender: UIButton
@objc func objectTapped(_ sender: UIButton){
print("Object With Tag \(tag)")
}
}
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
//1. Check We Have The Image Anchor
guard let imageAnchor = anchor as? ARImageAnchor else { return }
//2. Get The Reference Image
let referenceImage = imageAnchor.referenceImage
//1. Create The Plane Geometry With Our Width & Height Parameters
let planeGeometry = SCNPlane(width: referenceImage.physicalSize.width,
height: referenceImage.physicalSize.height)
//2. Create A New Material
let material = SCNMaterial()
DispatchQueue.main.async {
//3. Create The New Clickable View
let clickableElement = ClickableView(frame: CGRect(x: 0, y: 0,
width: 300,
height: 300))
clickableElement.tag = 1
//4. Add The Clickable View As A Materil
material.diffuse.contents = clickableElement
}
//5. Create The Plane Node
let planeNode = SCNNode(geometry: planeGeometry)
planeNode.geometry?.firstMaterial = material
planeNode.opacity = 0.25
planeNode.eulerAngles.x = -.pi / 2
//6. Add It To The Scene
node.addChildNode(planeNode)
}