将工具栏重构为另一个文件

Refactoring out a toolbar to another file

经验丰富的 Swift UI 套件编码器,但绝对不会使用 SwiftUI。假设我有以下代码:

HStack {
    Button(
        action: { self.createNode() },
        label: {
            Text("ADD").bold().font(.system(size: 40)).frame(width: 200, height: 80).background(Color.yellow).cornerRadius(5)
    })
    Spacer()
    Button(
        action: { self.deleteNode() },
        label: {
            Text("DELETE").bold().font(.system(size: 40)).frame(width: 200, height: 80).background(Color.yellow).cornerRadius(5)
    })
}

一个非常简单的工具栏。但是现在,让我们再添加 (a) 四个 Buttons 并将其放入 "root" 视图中。在 UIKit 中我们称之为 大型视图控制器

我正在尝试将整个 HStack 移出根视图,但 action 出现问题。我知道我可以创建 ViewModifiers、自定义视图,并且 - 至少在某种程度上 - 将一些东西移动到扩展中。但是我无法将此扩展 "lock, stock, and barrel" 移动到其他任何地方。我一直在尝试将 createNode()deleteNode() 移动到其他任何地方。

我确定是我在尝试将方钉 (UIKit) 装入圆孔 (SwiftUI),但是 none WWDC 会议或其他资源我我发现似乎在为我指明正确的方向。我错过了什么?

编辑:

以下是这两个操作 - 它们工作正常。

@State var nodes: [Node] = []

func createNode() {
    let newNodeName = nodes.count + 1
    let newNode = Node(name: newNodeName)
    nodes.append(newNode)
}
func deleteNode() {
    if nodes.count != 0 {
        nodes.remove(at: nodes.count - 1)
    }
}

我的问题不是关于Swift,也不是关于维护数组。它是关于 SwiftUI,以及如何 "refactor" 我当前的 140 行文件变成更小的东西 - 在这种情况下通过删除 "top bar" HStack 到它自己的文件 Xcode.

这个水平堆叠的按钮最终将编号为 5(它是一个 iPad 唯一的应用程序),我的问题是如何移动按钮 "actions",正确键入 () -> Void

更多,这是整个文件的简化视图(请原谅使用降价,我找不到更好的方式来表示它):

HStack    
    VStack
       HStack  <-- Contains the Buttons that add/delete the nodes
       HStack
       HStack  <-- Contains the nodes themselves, will soon contain thumbnails    
    List

如果我的 HStack 由 Text 视图的水平行组成,它工作正常。但是当我尝试将 Button(无论是嵌套在 HStack 中还是独立的)移动到一个单独的文件中时,我遇到了构建问题。

我建议将操作和 @State 重构到视图模型中。

final class ViewModel: BindableObject {

    let didChange = PassthroughSubject<Void, Never>()

    var nodes: [Node] = [] {
        didSet {
            didChange.send(())
        }
    }

    func createNode() {
        let newNodeName = nodes.count + 1
        let newNode = Node(name: newNodeName)
        nodes.append(newNode)
    }

    func deleteNode() {
        if nodes.count != 0 {
            nodes.remove(at: nodes.count - 1)
        }
    }

}

所有需要访问动作的视图,需要声明这个属性:

@EnvironmentObject var viewModel: ViewModel

有关 @EnvironmentObject here 的更多信息。

每当节点发生变化时,所有声明环境对象的子视图都将被重绘。

你只需要在容器视图中设置环境对象,它就会被传递给所有的子视图。

例如

ContentView().environmentObject(ViewModel())

您的视图层次结构:

HStack    
    VStack
       HStack  <- @EnvironmentObject (will call the actions)
       HStack
       HStack  <- @EnvironmentObject (will use viewModel.nodes to display nodes)
    List