Como dice Martin , si miras la documentación de VStack
's init(alignment:spacing:content:)
, puedes ver que el content:
parámetro tiene el atributo @ViewBuilder
:
init(alignment: HorizontalAlignment = .center, spacing: Length? = nil,
@ViewBuilder content: () -> Content)
Este atributo se refiere al ViewBuilder
tipo, que si observa la interfaz generada, se ve así:
@_functionBuilder public struct ViewBuilder {
public static func buildBlock() -> EmptyView
public static func buildBlock(_ content: Content) -> Content
where Content : View
}
El @_functionBuilder
atributo es parte de una característica no oficial llamada " constructores de funciones ", que se ha incluido en Swift evolution aquí e implementado especialmente para la versión de Swift que se envía con Xcode 11, lo que permite su uso en SwiftUI.
Marcar un tipo @_functionBuilder
permite que se utilice como un atributo personalizado en varias declaraciones como funciones, propiedades calculadas y, en este caso, parámetros del tipo de función. Tales declaraciones anotadas usan el generador de funciones para transformar bloques de código:
- Para las funciones anotadas, el bloque de código que se transforma es la implementación.
- Para las propiedades calculadas anotadas, el bloque de código que se transforma es el captador.
- Para los parámetros anotados del tipo de función, el bloque de código que se transforma es cualquier expresión de cierre que se le pasa (si corresponde).
La forma en que un constructor de funciones transforma el código se define mediante la implementación de métodos de construcción como buildBlock
, que toma un conjunto de expresiones y las consolida en un solo valor.
Por ejemplo, ViewBuilder
implementa buildBlock
de 1 a 10 View
parámetros conformes, consolidando múltiples vistas en una sola TupleView
:
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension ViewBuilder {
public static func buildBlock<Content>(_ content: Content)
-> Content where Content : View
public static func buildBlock<C0, C1>(_ c0: C0, _ c1: C1)
-> TupleView<(C0, C1)> where C0 : View, C1 : View
public static func buildBlock<C0, C1, C2>(_ c0: C0, _ c1: C1, _ c2: C2)
-> TupleView<(C0, C1, C2)> where C0 : View, C1 : View, C2 : View
}
Esto permite que un conjunto de expresiones de vista dentro de un cierre pasado al VStack
inicializador se transforme en una llamada a buildBlock
que toma el mismo número de argumentos. Por ejemplo:
struct ContentView : View {
var body: some View {
VStack(alignment: .leading) {
Text("Hello, World")
Text("Hello World!")
}
}
}
se transforma en una llamada a buildBlock(_:_:)
:
struct ContentView : View {
var body: some View {
VStack(alignment: .leading) {
ViewBuilder.buildBlock(Text("Hello, World"), Text("Hello World!"))
}
}
}
dando como resultado que el tipo de resultado opaco some View
sea satisfecho por TupleView<(Text, Text)>
.
Observará que ViewBuilder
solo define buildBlock
hasta 10 parámetros, por lo que si intentamos definir 11 subvistas:
var body: some View {
VStack(alignment: .leading) {
Text("Hello, World")
Text("Hello World!")
Text("Hello World!")
Text("Hello World!")
Text("Hello World!")
Text("Hello World!")
Text("Hello World!")
Text("Hello World!")
Text("Hello World!")
Text("Hello World!")
Text("Hello World!")
}
}
obtenemos un error de compilador, ya que no hay un método de compilador para manejar este bloque de código (tenga en cuenta que debido a que esta característica aún está en proceso, los mensajes de error que la rodean no serán tan útiles).
En realidad, no creo que las personas se encuentren con esta restricción con tanta frecuencia, por ejemplo, el ejemplo anterior se serviría mejor usando la ForEach
vista:
var body: some View {
VStack(alignment: .leading) {
ForEach(0 ..< 20) { i in
Text("Hello world \(i)")
}
}
}
Sin embargo, si necesita más de 10 vistas definidas estáticamente, puede solucionar fácilmente esta restricción utilizando la Group
vista:
var body: some View {
VStack(alignment: .leading) {
Group {
Text("Hello world")
}
Group {
Text("Hello world")
}
}
ViewBuilder
también implementa otros métodos de creación de funciones como:
extension ViewBuilder {
public static func buildEither<TrueContent, FalseContent>(first: TrueContent)
-> ConditionalContent<TrueContent, FalseContent>
where TrueContent : View, FalseContent : View
public static func buildEither<TrueContent, FalseContent>(second: FalseContent)
-> ConditionalContent<TrueContent, FalseContent>
where TrueContent : View, FalseContent : View
}
Esto le da la capacidad de manejar declaraciones if:
var body: some View {
VStack(alignment: .leading) {
if .random() {
Text("Hello World!")
} else {
Text("Goodbye World!")
}
Text("Something else")
}
}
que se transforma en:
var body: some View {
VStack(alignment: .leading) {
ViewBuilder.buildBlock(
.random() ? ViewBuilder.buildEither(first: Text("Hello World!"))
: ViewBuilder.buildEither(second: Text("Goodbye World!")),
Text("Something else")
)
}
}
(emitiendo llamadas redundantes de 1 argumento ViewBuilder.buildBlock
para mayor claridad).
@ViewBuilder
developer.apple.com/documentation/swiftui/viewbuilder .