簡體   English   中英

ARKit添加UIView xib文件Swift 4

[英]ARKit adding UIView xib file Swift 4

當我檢測到我的錨點時,我添加了一個xib UIView文件作為我的SCNBox的材料但是當我解雇這個viewcontroller時,應用程序凍結,這是我的代碼:

var detectedDataAnchor: ARAnchor?
var myView = UIView()

override func viewDidLoad() {
  super.viewDidLoad()
  myView = (Bundle.main.loadNibNamed("ARViewOne", owner: nil, options: nil)![0] as? UIView)!
}



func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {

      if self.detectedDataAnchor?.identifier == anchor.identifier {
        let node = SCNNode()
        let box = SCNBox(width: 0.1, height: 0.1, length: 0.1,
                                 chamferRadius: 0.0)
        let imageMaterial = SCNMaterial()

        imageMaterial.diffuse.contents = myView
        box.materials = [imageMaterial]
        let cube = SCNNode(geometry: box)
        node.addChildNode(cube)
        return node

    }
   return nil
}

 @IBAction func back(_ sender: UIButton) {
     // here the app freezes
     navigationController?.popViewController(animated: true)
  }

當回到我的VC時,我的應用程序不響應任何觸摸事件

好的,首先是第一件事:

您無法將UIView添加為SCNMaterialProperty contents 這里閱讀。

我在這里猜測,但這可能是你看到的凍結/崩潰的原因,因為當你彈出它所在的控制器時場景會被取消並試圖清理那些實際上被破壞的材料(它會把它視為一些其他類型)。

一般來講就行

myView = (Bundle.main.loadNibNamed("ARViewOne", owner: nil, options: nil)![0] as? UIView)!

看起來像你對此有點新鮮(這完全沒問題!)。 你是強行解纏和強制施放(以一種奇怪的方式, as? UIView!基本上就像as! UIView ),這總是危險的。 你確定你的xib中的第一個頂級對象是UIView嗎? 此外,最初實例化的UIView (在var myView = UIView()中從不使用,為什么不在這里使用可選的(你可以使用一個隱式解包的,雖然我自己不是這個的粉絲)。這樣做怎么樣方式:

var myViewImage: UIImage?

override func viewDidLoad() {
    super.viewDidLoad()
    let myView = Bundle.main.loadNibNamed("ARViewOne", owner: nil, options: nil).first as? UIView
    myViewImage = myView?.asImage()
}

func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {

    guard let image = myViewImage, self.detectedDataAnchor?.identifier == anchor.identifier else { return nil }

    let node = SCNNode()
    let box = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0.0)
    let imageMaterial = SCNMaterial()
    imageMaterial.diffuse.contents = image
    box.materials = [imageMaterial]
    let cube = SCNNode(geometry: box)
    node.addChildNode(cube)
    return node
}

// this would be outside your controller class, i.e. the top-level of a swift file
extension UIView {
    func asImage() -> UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: bounds)
        return renderer.image { rendererContext in
            layer.render(in: rendererContext.cgContext)
        }
    }
}

UIView擴展來自這里 ,所以感謝Naveed J.)

這假定a)你的xib確實是正確的b)你只是想要“視圖的外觀”,即節點上的一種“屏幕截圖”。 它將是一個靜止圖像,顯然,你不能將視圖的整個行為(附加的手勢識別器等)放到這樣的節點上。 對於這些事情我建議打開一個新問題(在你完成這件事之后,你滿意)。

編輯以回應Fabio的評論:

我忘了在不同的隊列/線程上調用renderer(_:nodeFor:) 您確實應該僅在主線程上生成視圖圖像,因為警告“UIView.bounds必須僅從主線程使用”說。 我更改了上面的代碼以反映這一點。 請記住,swift本身並不是線程安全的,因為在渲染器開始發送renderer(_:nodeFor:)之前,將調用viewDidLoad() (在主線程上)。 因此,您可以確保在檢索圖像時不會出現訪問沖突,但這意味着您不應該在主線程的某處修改所述圖像。

如果你需要以某種方式改變圖像/動態創建它有一個替代方案。 我個人使用委托的另一種方法renderer(_:didAdd:for:) ,而不是在renderer(_:nodeFor:)自己創建節點renderer(_:nodeFor:) 此方法不期望返回值,因此它看起來像這樣:

var myView: UIView?

override func viewDidLoad() {
    super.viewDidLoad()
    myView = Bundle.main.loadNibNamed("ARViewOne", owner: nil, options: nil).first as? UIView
}

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    DispatchQueue.main.async {
        self.attachCustomNode(to: node, for: anchor)
    }
}

func attachCustomNode(to node: SCNNode, for anchor: ARAnchor) {
    guard let theView = myView, self.detectedDataAnchor?.identifier == anchor.identifier else { return }

    let box = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0.0)
    let imageMaterial = SCNMaterial()
    imageMaterial.diffuse.contents = theView.asImage()
    box.materials = [imageMaterial]
    let cube = SCNNode(geometry: box)
    node.addChildNode(cube)
    return node
}

通過從renderer(_:nodeFor:) ,ARKit會為您創建一個空節點。 基本上你在renderer(_:nodeFor:)做的事情是相同的renderer(_:nodeFor:) 然后它調用renderer(_:didAdd:for:) ,它不期望返回值。 所以從那里我“切換到主隊列”並添加必要的孩子。

我很確定這是有效的,IIRC SceneKit會自動在其渲染線程中批量添加(即它在節點的addChildNode()內實際執行的操作addChildNode() 因此,上面的所有“准備工作”都發生在主線程上,您可以安全地使用視圖的bounds來創建圖像。 節點設置完成后,當您添加節點時,SceneKit會在其渲染線程上正確地執行此操作。

應該做的就是簡單地把一個DispatchQueue.main.async您的內部renderer(_:nodeFor:)方法,改變材質的漫內容在那里。 這意味着您可以從不同的線程修改添加的節點,但是您不能確定它是否可以正常工作。 它可能看起來像是有效的,我不知道SceneKit是否會以某種方式防范它,但我不會依賴它。 無論如何,這都是糟糕的風格。

我希望這一切足以使您的項目有效。 :)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM