【问题标题】:Swift Add Button to SCNNodeSwift 将按钮添加到 SCNNode
【发布时间】:2018-08-10 00:51:44
【问题描述】:

我正在使用 ARKit 和图像检测。 现在我有一个应用程序可以检测图像并将平面放置在检测到的对象所在的屏幕上。

如何在飞机上添加可点击元素,例如按钮。我想在每个检测到的对象上都有一个点击事件。

这是我的渲染器函数的样子:

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)
        }
    }

【问题讨论】:

    标签: swift xcode scenekit arkit xcode9-beta


    【解决方案1】:

    有几种方法可以解决这个问题:

    1:标准方法

    在您的 delegate 方法中,为每个节点指定一个名称,如下所示(显然,如果您有很多节点,您可能希望根据需要将它们存储在数组或字典中):

     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()
    
        material.diffuse.contents = UIColor.red
    
        //3. Create The Plane Node
        let planeNode = SCNNode(geometry: planeGeometry)
    
        planeNode.geometry?.firstMaterial = material
    
        planeNode.opacity = 0.25
    
        planeNode.eulerAngles.x = -.pi / 2
    
        //4. Add A Name To The Node
        planeNode.name = "I Was Clicked"
    
        //5. Add It To The Scene
        node.addChildNode(planeNode)
    
    }
    

    然后使用touchesBegan,执行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)
    
    }
    

    2:一种有趣的方法:

    UIKit 元素实际上可以添加为SCNGeometry's Material。我个人没有看到很多人使用这种方法,但它可能对任何想要将UIKitARKit 合并的人有用。

    创建自定义 UIButton 例如:

    /// 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)
    }
    

    这应该让你开始......

    【讨论】:

    • 谢谢!这对我帮助很大。
    • 第二种方法非常好。对于任何尝试实现它并希望通过呈现/推送导航到其他屏幕的人,请注意,SceneKit 会为您分配给材质的每个此类 UIKit 元素创建新的UIWindow 对象,这就是它发送要处理的事件的方式。因此,请注意进行必要的清理以避免不必要的行为(例如,您可以遍历 UIApplication.shared.windows 并隐藏不是关键的窗口)。
    猜你喜欢
    • 1970-01-01
    • 2019-08-11
    • 1970-01-01
    • 2016-05-15
    • 1970-01-01
    • 1970-01-01
    • 2017-12-17
    • 2016-12-20
    • 2018-11-04
    相关资源
    最近更新 更多