【问题标题】:Refactoring out a toolbar to another file将工具栏重构为另一个文件
【发布时间】:2019-06-12 23:45:23
【问题描述】:

经验丰富的 Swift UIKit 编码器,但绝对不会使用 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 并将其放入“根”视图中。在 UIKit 中我们称之为 大规模视图控制器

我正在尝试将整个 HStack 移出根视图,但我遇到了 action 的问题。我知道我可以创建ViewModifiers、自定义视图,并且 - 至少在某种程度上 - 将一些东西移动到扩展中。但我无法将这个扩展“锁、库存和桶”移动到其他任何地方。我一直试图将createNode()deleteNode() 移动到其他任何地方。

我确定是我试图将方形钉 (UIKit) 装入圆孔 (SwiftUI),但我发现的 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,以及如何将我当前的 140 行文件“重构”成更小的东西——在这种情况下,通过将“顶栏”HStack 删除到它自己在 Xcode 中的文件中。

这个水平堆叠的 Buttons 最终将排在 5 号(这是一个仅限 iPad 的应用程序),我的问题是如何移动 Button 的“动作”,正确输入 () -> 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 中或独立的)移动到单独的文件中时,我会遇到构建问题。

【问题讨论】:

  • createdNode/deleteNode 应该做什么?
  • 我认为这与问题无关,但我会对其进行编辑以包含。

标签: swift swiftui


【解决方案1】:

我建议将操作和@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

关于@EnvironmentObjecthere的更多信息。

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

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

例如

ContentView().environmentObject(ViewModel())

您的视图层次结构

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

【讨论】:

  • 看来我错了——它一个 Swift 问题!当我尝试实例化 EnvironmentObject 时,我正在处理 SceneDelegate 中的 SIGABRT。 (我也在深入挖掘 @type 和 Combine ......它是一种范式转变。一旦我完成工作,期待一个可接受的答案。非常感谢你为我指出正确的方向 - 这正是我想要的.
  • 有关属性包装器和 $ 变量的更多信息,请参阅thisSwift 进化帖子。
  • 再次感谢。我真的在使用 iOS“单一视图”应用程序,所以我的单一真相来源是罐头 ContentView。现在我认为@EnvironmentObject 是多余的,并决定使用简单的@State@Binding。我通过“动手”学得最好,你让我对我需要学习的东西进行挖掘。如果我认为这是正确的路线,我会考虑使用@EnvironmentObject。 (我是老派,希望我的变量范围尽可能有限。)
猜你喜欢
  • 1970-01-01
  • 2021-10-01
  • 1970-01-01
  • 2019-09-25
  • 2014-10-11
  • 1970-01-01
  • 2012-12-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多