Я пытаюсь реализовать очень простое приложение SwiftUI с базовой сценой / представлением SceneKit. Кнопки в представлении SwiftUI должны манипулировать содержимым сцены, и наоборот, содержимое сцены должно определять, являются ли кнопки активными или неактивными.
Да, я читал и смотрел сессии разработчиков Apple 226: поток данных через SwiftUI и 231: интеграция SwiftUI . И я прошел через учебники по Apple . Но я немного потерян здесь. Любые советы и указания приветствуются.
Вот код:
У меня есть MainView, который использует SceneKit SCNView
с HUDView
сверху:
import SwiftUI
struct MainView: View {
var body: some View {
ZStack {
SceneView()
HUDView()
}
}
}
SceneView
включает SCNView
через протокол UIViewRepresentable
. Сцена имеет две функции для добавления и удаления рамки со сцены:
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
связывает кнопки с действиями для добавления и удаления рамки из базовой сцены. Если объект box находится в сцене, кнопка добавления должна быть неактивной, должна быть активна только кнопка удаления:
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 о своем содержимом (поле присутствует или нет), чтобы оно могло устанавливать активное / неактивное состояние кнопок?
Каков наилучший подход к этой задаче? Должен ли я реализовать Coordinator
в SceneView
? Должен ли я использовать отдельный SceneViewController
, реализованный по протоколу UIViewControllerRepresentable
?