要使用自定义SwiftUI视图作为SceneKit中SCNNode
的几何体,可以从将SwiftUI视图渲染为UIImage
开始.这可以通过创建视图的快照来完成.
Once you have the UIImage
, you can apply it as a texture to the SCNMaterial
of your SCNNode
.
See for instance "Updating SCNMaterial
texture in SceneKit" from Benoit Layer.
最后,创建一个geometry(例如,SCNPlane
),并将具有自定义纹理的material 应用于此几何体.
这一过程将是:
[ SwiftUI View ]
│
├─ Render to UIImage
│
[ UIImage ]
│
├─ Apply as Texture to Material
│
[ SCNMaterial ]
│
├─ Attach to SCNPlane or other geometry
│
[ SCNNode ]
您的SwiftUI View to UIImage
将是:
import SwiftUI
extension View {
func snapshot() -> UIImage {
let controller = UIHostingController(rootView: self)
let view = controller.view
let targetSize = controller.view.intrinsicContentSize
view?.bounds = CGRect(x: 0, y: 0, width: targetSize.width, height: targetSize.height)
view?.backgroundColor = .clear
let renderer = UIGraphicsImageRenderer(size: targetSize)
return renderer.image { _ in
view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
}
}
}
然后应用自定义几何体:
import SceneKit
import SwiftUI
// Example SwiftUI View
struct CustomView: View {
var body: some View {
Text("Hello World")
.frame(width: 100, height: 100)
.background(Color.red)
}
}
// Convert SwiftUI view to UIImage
let image = CustomView().snapshot()
// Create material with this image
let material = SCNMaterial()
material.diffuse.contents = image
// Create a geometry, e.g., SCNPlane
let plane = SCNPlane(width: 1.0, height: 1.0)
plane.materials = [material]
// Create a SCNNode with this geometry
let customNode = SCNNode(geometry: plane)
// Position and add the custom node as needed
customNode.position = SCNVector3(x: 0, y: 0, z: 0)
dotNode.addChildNode(customNode)
但是:SwiftUI视图呈现为静态图像,因此它们在SceneKit上下文中不是交互式的.
警告=== AttributeGraph: cycle detected through attribute 1517200 ===
表示SwiftUI中存在循环依赖或与视图布局相关的问题.当视图的状态发生变化,导致SwiftUI不断重新计算视图的布局,从而导致无限循环时,就会发生这种情况.
所以试着简化SwiftUI视图:暂时用一个非常简单的视图(比如一个Text
元素)替换SwiftUI视图,以确认是否是视图的复杂性导致了问题.
并确保您没有使用任何可能导致循环依赖的视图修改器,例如那些依赖于视图自身大小或布局的修改器.在将SwiftUI视图渲染为UIImage
之前,为其设置明确的帧大小.这有助于防止与可能导致循环的大小计算相关的问题.
extension View {
func snapshot() -> UIImage {
let controller = UIHostingController(rootView: self.frame(width: 100, height: 100))
let view = controller.view
let targetSize = CGSize(width: 100, height: 100)
view?.bounds = CGRect(origin: .zero, size: targetSize)
view?.backgroundColor = .clear
let renderer = UIGraphicsImageRenderer(size: targetSize)
return renderer.image { _ in
view?.drawHierarchy(in: CGRect(origin: .zero, size: targetSize), afterScreenUpdates: true)
}
}
}
确保在主线程上执行所有与UI相关的任务,包括将SwiftUI视图呈现为UIImage
.
DispatchQueue.main.async {
let image = CustomView().snapshot()
// Create material with this image
let material = SCNMaterial()
material.diffuse.contents = image
// Rest of the code to create and add SCNNode
//
}