Como Martin diz , se você olhar a documentação de VStack
's init(alignment:spacing:content:)
, verá que o content:
parâmetro tem o atributo @ViewBuilder
:
init(alignment: HorizontalAlignment = .center, spacing: Length? = nil,
@ViewBuilder content: () -> Content)
Este atributo se refere ao ViewBuilder
tipo, que se você olhar para a interface gerada, é semelhante a:
@_functionBuilder public struct ViewBuilder {
public static func buildBlock() -> EmptyView
public static func buildBlock(_ content: Content) -> Content
where Content : View
}
O @_functionBuilder
atributo é parte de um recurso não oficial chamado " construtores de função ", que foi lançado na evolução do Swift aqui , e implementado especialmente para a versão do Swift que acompanha o Xcode 11, permitindo que seja usado no SwiftUI.
A marcação de um tipo @_functionBuilder
permite que ele seja usado como um atributo personalizado em várias declarações, como funções, propriedades calculadas e, neste caso, parâmetros do tipo de função. Essas declarações anotadas usam o construtor de função para transformar blocos de código:
- Para funções anotadas, o bloco de código que é transformado é a implementação.
- Para propriedades computadas anotadas, o bloco de código que é transformado é o getter.
- Para parâmetros anotados do tipo de função, o bloco de código que é transformado é qualquer expressão de encerramento que é passada para ele (se houver).
A maneira pela qual um construtor de função transforma o código é definida por sua implementação de métodos de construtor , como buildBlock
, que pega um conjunto de expressões e as consolida em um único valor.
Por exemplo, ViewBuilder
implementa buildBlock
de 1 a 10 View
parâmetros em conformidade, consolidando várias visualizações em uma única 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
}
Isso permite que um conjunto de expressões de exibição dentro de um encerramento passado ao VStack
inicializador de seja transformado em uma chamada para buildBlock
que leva o mesmo número de argumentos. Por exemplo:
struct ContentView : View {
var body: some View {
VStack(alignment: .leading) {
Text("Hello, World")
Text("Hello World!")
}
}
}
é transformado em uma chamada para buildBlock(_:_:)
:
struct ContentView : View {
var body: some View {
VStack(alignment: .leading) {
ViewBuilder.buildBlock(Text("Hello, World"), Text("Hello World!"))
}
}
}
resultando no tipo de resultado opaco some View
sendo satisfeito por TupleView<(Text, Text)>
.
Você notará que ViewBuilder
define apenas buildBlock
até 10 parâmetros, portanto, se tentarmos definir 11 subvisualizações:
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!")
}
}
obtemos um erro do compilador, pois não há nenhum método do construtor para lidar com esse bloco de código (observe que, como esse recurso ainda é um trabalho em andamento, as mensagens de erro em torno dele não serão tão úteis).
Na realidade, não acredito que as pessoas vão se deparar com essa restrição com tanta frequência, por exemplo, o exemplo acima seria melhor servido usando a ForEach
visualização:
var body: some View {
VStack(alignment: .leading) {
ForEach(0 ..< 20) { i in
Text("Hello world \(i)")
}
}
}
No entanto, se você precisar de mais de 10 visualizações definidas estaticamente, pode facilmente contornar essa restrição usando a Group
visualização:
var body: some View {
VStack(alignment: .leading) {
Group {
Text("Hello world")
}
Group {
Text("Hello world")
}
}
ViewBuilder
também implementa outros métodos construtores de função, 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
}
Isso dá a ele a capacidade de lidar com declarações if:
var body: some View {
VStack(alignment: .leading) {
if .random() {
Text("Hello World!")
} else {
Text("Goodbye World!")
}
Text("Something else")
}
}
que se transforma em:
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")
)
}
}
(emitindo chamadas redundantes de 1 argumento para ViewBuilder.buildBlock
para maior clareza).
@ViewBuilder
developer.apple.com/documentation/swiftui/viewbuilder .