ARKit 添加 UIView xib 文件 Swift 4
Posted
技术标签:
【中文标题】ARKit 添加 UIView xib 文件 Swift 4【英文标题】:ARKit adding UIView xib file Swift 4 【发布时间】:2018-10-03 22:08:21 【问题描述】:当我检测到我的锚点时,我向我的 SCNBox 添加了一个 xib UIView 文件作为材料,但是当我关闭这个视图控制器时,应用程序冻结了,这是我的代码:
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 时,我的应用没有响应任何触摸事件
【问题讨论】:
【参考方案1】:好的,那么首先:
您不能将UIView
添加为contents
或SCNMaterialProperty
。阅读here。
我在这里猜测,但这可能是您看到的冻结/崩溃的原因,因为当您弹出它所在的控制器并尝试清理实际损坏的材料时,场景会被破坏(它会处理它作为其他类型)。
一般来说行
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
扩展名取自 here,感谢 Naveed J.)
这假设a)您的xib确实是正确的并且b)您只想要“视图的外观”,即节点上的一种“屏幕截图”。这将是一个静止图像,显然,您无法将视图的整个行为(附加的手势识别器等)放到这样的节点上。对于这些事情,我建议您提出一个新问题(在您满意地完成此操作之后)。
根据 Fabio 的评论进行编辑:
我忘记了 renderer(_:nodeFor:)
在不同的队列/线程上被调用。您确实应该只在主线程上生成视图图像,因为警告“UIView.bounds must be used only from main thread”说。我更改了上面的代码以反映这一点。请记住,swift 本质上不是线程安全的,因为viewDidLoad()
将在渲染器开始发送renderer(_:nodeFor:)
调用之前(在主线程上)被调用。因此,您可以确保在检索图像时不会遇到访问冲突,但这意味着您不应该在主线程的某处修改所述图像。
如果您需要以某种方式更改图像/动态创建图像,则可以使用另一种方法。我个人会使用代理的另一种方法renderer(_:didAdd:for:)
,而不是自己在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(_:didAdd:for:)
,它不期望返回值。所以从那里我“切换到主队列”并添加必要的孩子。
我很确定这可行,IIRC SceneKit 会在其渲染线程中自动批量添加(即它在节点的addChildNode()
中实际执行的操作)。因此,上述所有“准备工作”都发生在主线程上,您可以安全地使用视图的bounds
创建图像。节点已设置好,当您添加它时,SceneKit 会在其渲染线程上正确执行此操作。
您应该不做的只是在您的renderer(_:nodeFor:)
方法中添加一个DispatchQueue.main.async
并在其中更改材质的漫反射内容。这意味着您从不同的线程修改添加的节点,但您不能确定它可以在没有冲突的情况下工作。它可能看起来很有效,我不知道 SceneKit 是否会以某种方式防范它,但我不会依赖它。无论如何,这是一种糟糕的风格。
我希望这一切足以让您的项目顺利进行。 :)
【讨论】:
这是一个很好的答案,但我有这个胎面警告,当我使用扩展将 UIView 呈现为图像时...... UIView.bounds 必须从主线程使用,你知道如何解决这个问题?以上是关于ARKit 添加 UIView xib 文件 Swift 4的主要内容,如果未能解决你的问题,请参考以下文章