使用@ViewBuilder创建支持多个子视图的视图

ben*_*nto 6 swift swiftui

SwiftUI中的某些视图(例如VStack和HStack)支持将多个视图作为子视图,如下所示:

VStack {
  Text("hello")
  Text("world")
}
Run Code Online (Sandbox Code Playgroud)

从我收集,他们使用ViewBuilder使之成为可能的解释在这里

我们如何使用@ViewBuilder创建自己的支持多个子视图的视图?例如,假设我要创建一个Layout接受任意子级的View,如下所示:

struct Layout : View {
  let content: Some View 

  var body : some View {
    VStack {
      Text("This is a layout")
      content()
    }
  } 
}
Run Code Online (Sandbox Code Playgroud)

知道如何在SwiftUI中实现此模式吗?

Mat*_*ini 9

这是一个不执行任何操作的示例视图,仅用于演示如何使用@ViewBuilder

struct Passthrough<Content>: View where Content: View {

    let content: () -> Content

    init(@ViewBuilder content: @escaping () -> Content) {
        self.content = content
    }

    var body: some View {
        content()
    }

}
Run Code Online (Sandbox Code Playgroud)

用法:

Passthrough {
    Text("one")
    Text("two")
    Text("three")
}
Run Code Online (Sandbox Code Playgroud)

  • 很好的解释。但我有一个问题。是否可以像 TabView 那样只显示子视图之一?以你的例子为例,你有 3 个文本,但我只想一次只显示其中一个。这意味着我需要获取 ViewBuilder 中内容的子级。是否可以? (3认同)
  • @bento `View` 协议有一个关联类型,称为 `Body`,因此你不能说 `let content: () -&gt; View` - 试试看,你会得到一个编译器错误。`@escaping` 意味着闭包将比它所在的上下文(构造函数)更长寿,并且正如在 `body` 中调用的那样。 (2认同)

Den*_*Fav 7

使用VStack我们需要@ViewBuilder用于我们的内容参数的声明。这是一个闭包,但它不应该是 @escaping 如果我们只需要来自它的数据,那么存储闭包就不好。我假设来自 Apple 声明。

我也认为这@inlinable很重要,因为:

@inlinable 属性将函数体作为模块接口的一部分导出,使其在被其他模块引用时可供优化器使用。 更多信息在这里

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
            }
        } 
    }
Run Code Online (Sandbox Code Playgroud)

要使用它:

Layout {           
            Text("1")
            VStack {
                Text("1")
                Text("2")
            }
        }
Run Code Online (Sandbox Code Playgroud)

UPD: 正如马特奥·帕西尼( Matteo Pacini)指出的那样,这是关于@escaping.

我们需要使用@escapingDynamicViewContent意见。 @escaping用于接受集合(数组、范围等)的视图结构的 Apple 视图结构。因为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
Run Code Online (Sandbox Code Playgroud)

  • Re `@escaping`,Apple 在 List 中使用它,例如在初始化程序 `init(_:action:rowContent:)` 中 - 另一个考虑是你正在创建视图结构 `before` body 被调用。当然,这取决于用例,但说不得使用可能会产生误导。 (3认同)