如何访问 SwiftUI 中的子视图?

How to access to the children views in SwiftUI?

我正在做 SwiftUI,感觉它和 React 很像。刚才我正在自定义 SwiftUI 的 Button 并且遇到无法动态访问 Button 的子视图的问题 以下代码是我要做的:

struct FullButton : View {
  var action: () -> Void
  var body: some View {
    Button(action: action) {
      // render children views here even what is that
      children
    }
  }
}

和用法:

VStack {
  FullButton(action: {
    print('touched')
  }) {
    Text("Button")
  }
}

请问我是不是理解错了?


更新

取决于我试过的@graycampbell 的回答

struct FullButton<Label> where Label : View {
    var action: () -> Void
    var label: () -> Label

    init(action: @escaping () -> Void, @ViewBuilder label: @escaping () -> Label) {
        self.action = action
        self.label = label
    }

    var body: some View {
        Button(action: action, label: label)
    }
}

所以 FullButton 看起来和它本身一样好。但是此时我在使用中还有另一个编译错误。

VStack {
    FullButton(action: { print("touched") }) {
        Text("Fullbutton")
    }
}

错误是Referencing initializer 'init(alignment:spacing:content:)' on 'VStack' requires that 'FullButton<Text>' conform to 'View'
这意味着FullButton现在还没有returnbody吗?
我不确定这是为什么,因为 FullButton 仍然扩展 View class.
请告诉我 class.

类型的正确 body 定义是什么

如果我正确理解你的问题,这就是你要查找的内容:

struct FullButton<Label>: View where Label: View {
    var action: () -> Void
    var label: () -> Label

    var body: some View {
        Button(action: self.action, label: self.label)
    }
}

这将允许您传递要在按钮上显示的任何内容,这意味着您在此处的代码现在可以工作了:

FullButton(action: {
    print("touched")
}) {
    Text("Button")
}

更新

在多次查看您的问题后,我意识到您的困惑源于对创建正常 Button.

时发生的事情的误解。

在下面的代码中,我正在创建一个 Button。该按钮有两个参数 - actionlabel.

Button(action: {}, label: {
    Text("Button")
})

如果我们查看 Button 的文档,我们会发现它是这样声明的:

struct Button<Label> where Label : View

如果我们再查看初始值设定项,我们会看到:

init(action: @escaping () -> Void, @ViewBuilder label: () -> Label)

actionlabel 都希望关闭。 action 期望 return 类型 Void 的闭包,label 期望 @ViewBuilder 类型 Label 的 return 闭包].正如在 Button 的声明中定义的那样,Label 是一个代表 View 的泛型,所以实际上,label 期望一个 return 是 [=32] 的闭包=].

这不是 Button 独有的。以HStack为例:

struct HStack<Content> where Content : View

init(alignment: VerticalAlignment = .center, spacing: Length? = nil, @ViewBuilder content: () -> Content)

ContentLabelButton.

中的作用相同

还有一点要注意 - 当我们创建这样的按钮时...

Button(action: {}) {
    Text("Button")
}

...我们实际上在做同样的事情:

Button(action: {}, label: {
    Text("Button")
})

在Swift中,当方法调用的最后一个参数是闭包时,我们可以省略参数标签,将闭包附加到右括号外。

在 SwiftUI 中,您不能将内容隐式传递给任何 ViewView 必须在其初始化程序中显式接受 @ViewBuilder 闭包。

因此,您不能将 @ViewBuilder 闭包传递给 FullButton,除非 FullButton 在其初始化程序中接受 @ViewBuilder 闭包作为参数,如开头所示我的回答。

有一个 ViewInspector 库使用 Swift 的反射从任何层次结构中提取 SwiftUI 视图。

let view = FullButton()
let button = try view.inspect().button()
let children = button.anyView().view(OtherView.Type)
// By the way, you can even tap the button programmatically:
try button.tap()