【发布时间】:2020-02-02 19:16:16
【问题描述】:
我正在尝试使用底层的 SceneKit 场景/视图来实现一个非常基本的 SwiftUI 应用程序。 SwiftUI 视图中的按钮应该操纵场景的内容,反之亦然,场景的内容应该确定按钮是活动的还是非活动的。
是的,我已经阅读并观看了 Apple 开发者会议 226: Data Flow through SwiftUI 和 231: Integrating SwiftUI。我通过 Apple tutorials 工作。但我在这里有点迷失。任何提示和方向表示赞赏。
代码如下:
我有一个 MainView,它使用 SceneKit SCNView,顶部带有 HUDView:
import SwiftUI
struct MainView: View {
var body: some View {
ZStack {
SceneView()
HUDView()
}
}
}
SceneView 通过UIViewRepresentable 协议集成了SCNView。场景有两个函数可以在场景中添加和移除盒子:
import SwiftUI
import SceneKit
struct SceneView: UIViewRepresentable {
let scene = SCNScene()
func makeUIView(context: Context) -> SCNView {
// create a box
scene.rootNode.addChildNode(createBox())
// code for creating the camera and setting up lights is omitted for simplicity
// …
// retrieve the SCNView
let scnView = SCNView()
scnView.scene = scene
return scnView
}
func updateUIView(_ scnView: SCNView, context: Context) {
scnView.scene = scene
// allows the user to manipulate the camera
scnView.allowsCameraControl = true
// configure the view
scnView.backgroundColor = UIColor.gray
// show statistics such as fps and timing information
scnView.showsStatistics = true
scnView.debugOptions = .showWireframe
}
func createBox() -> SCNNode {
let boxGeometry = SCNBox(width: 20, height: 24, length: 40, chamferRadius: 0)
let box = SCNNode(geometry: boxGeometry)
box.name = "box"
return box
}
func removeBox() {
// check if box exists and remove it from the scene
guard let box = scene.rootNode.childNode(withName: "box", recursively: true) else { return }
box.removeFromParentNode()
}
func addBox() {
// check if box is already present, no need to add one
if scene.rootNode.childNode(withName: "box", recursively: true) != nil {
return
}
scene.rootNode.addChildNode(createBox())
}
}
HUDView 将按钮与操作捆绑在一起,以在底层场景中添加和删除框。如果盒子对象在场景中,添加按钮应该是不活动的,只有删除按钮应该是活动的:
struct HUDView: View {
var body: some View {
VStack(alignment: .leading) {
HStack(alignment: .center, spacing: 2) {
Spacer()
ButtonView(action: {}, icon: "plus.square.fill", isActive: false)
ButtonView(action: {}, icon: "minus.square.fill")
ButtonView(action: {}) // just a dummy button
}
.background(Color.white.opacity(0.2))
Spacer()
}
}
}
这些按钮也相当简单,它们会执行一个操作,还可以选择一个图标以及它们的初始活动/非活动状态:
struct ButtonView: View {
let action: () -> Void
var icon: String = "square"
@State var isActive: Bool = true
var body: some View {
Button(action: action) {
Image(systemName: icon)
.font(.title)
.accentColor(isActive ? Color.white : Color.white.opacity(0.5))
}
.frame(width: 44, height: 44)
}
}
生成的应用程序非常简单:
SceneKit 场景渲染正确,我可以在屏幕上操作相机。
但是如何将按钮动作连接到相应的场景功能?如何让场景通知 HUDView 它的内容(框是否存在),以便它可以设置按钮的活动/非活动状态?
完成这项任务的最佳方法是什么?我应该在SceneView 中实现Coordinator 吗?我应该使用通过UIViewControllerRepresentable 协议实现的单独SceneViewController 吗?
【问题讨论】: