SwiftUI中的某些视图(例如VStack和HStack)支持将多个视图作为子视图,如下所示:
VStack {
Text("hello")
Text("world")
}
根据我的收集,他们使用ViewBuilder使之成为可能,如here所述。
我们如何使用@ViewBuilder创建自己的支持多个子视图的视图?例如,假设我要创建一个Layout
视图,该视图可以接受任意子级-
struct Layout : View {
let content: Some View
var body : some View {
VStack {
Text("This is a layout")
content()
}
}
}
有人知道如何在SwiftUI中实现此模式吗?
答案 0 :(得分:2)
这是一个不执行任何操作的示例视图,仅用于演示如何使用@ViewBuilder
。
struct Passthrough<Content>: View where Content: View {
let content: () -> Content
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
var body: some View {
content()
}
}
用法:
Passthrough {
Text("one")
Text("two")
Text("three")
}
答案 1 :(得分:1)
使用VStack
的声明,我们需要对内容参数使用@ViewBuilder
。它是一个闭包,但不应 @转义如果只需要闭包中的数据,则存储闭包就不好了。我认为是从Apple声明中得出的。
我还认为@inlinable
很重要,因为:
@inlinable属性将函数的主体导出为a的一部分 模块的接口,使其在优化器可用时 从其他模块引用。 More info here
struct Layout <Content> : View where Content : View {
var content: Content
@inlinable public init(@ViewBuilder content: () -> Content) {
self.content = content()
}
var body : some View {
VStack {
Text("This is a layout")
self.content
}
}
}
要使用它:
Layout {
Text("1")
VStack {
Text("1")
Text("2")
}
}
UPD:
正如Matteo Pacini所指出的关于@escaping
的误导性信息。
我们需要对@escaping
视图使用DynamicViewContent
。
@escaping
用于Apple的View结构,用于接受集合(数组,范围等)的视图结构。因为ForEach
实现了DynamicViewContent
-一种视图类型,可从基础数据集中生成视图。 List
在其初始值设定项中,ForEach
在其内容
public init<Data, RowContent>(_ data: Data, selection: Binding<Selection>?, action: @escaping (Data.Element.IdentifiedValue) -> Void, rowContent: @escaping (Data.Element.IdentifiedValue) -> RowContent) where Content == ForEach<Data, Button<HStack<RowContent>>>, Data : RandomAccessCollection, RowContent : View, Data.Element : Identifiable