如何在SwiftUI中访问子视图?

时间:2019-07-10 00:00:32

标签: 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现在还没有返回body
我不确定为什么会这样,因为FullButton仍然扩展了View类。
请让我知道该类型的类的正确body定义是什么。

2 个答案:

答案 0 :(得分:2)

如果我正确理解了您的问题,这就是您要寻找的东西:

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期望返回类型为Void的闭包,label期望返回类型为@ViewBuilder的{​​{1}}闭合。如Label的声明中所定义,Button是表示Label的泛型,因此,实际上View期望闭包返回label。 / p>

这并非View所独有。以Button为例:

HStack

struct HStack<Content> where Content : View init(alignment: VerticalAlignment = .center, spacing: Length? = nil, @ViewBuilder content: () -> Content) 在这里的作用与ContentLabel中的作用相同。

还有其他需要注意的地方-当我们创建这样的按钮时...

Button

...我们实际上正在执行以下操作:

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

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

在SwiftUI中,您不能将内容隐式传递给任何Button(action: {}, label: { Text("Button") }) View必须在其初始化程序中明确接受View闭包。

因此,除非@ViewBuilder在其初始值设定项中接受@ViewBuilder闭包作为参数,否则您不能将FullButton闭包传递给FullButton,如我开头所示。答案。

答案 1 :(得分:0)

有一个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()