Wie Martin sagt , wenn Sie sich die Dokumentation für VStack's ansehen init(alignment:spacing:content:), können Sie sehen, dass der content:Parameter das Attribut hat @ViewBuilder:
init(alignment: HorizontalAlignment = .center, spacing: Length? = nil,
@ViewBuilder content: () -> Content)
Dieses Attribut bezieht sich auf den ViewBuilderTyp, der bei Betrachtung der generierten Schnittstelle folgendermaßen aussieht:
@_functionBuilder public struct ViewBuilder {
public static func buildBlock() -> EmptyView
public static func buildBlock(_ content: Content) -> Content
where Content : View
}
Das @_functionBuilderAttribut ist Teil einer inoffiziellen Funktion namens " Funktionsersteller ", die hier in der Swift-Evolution vorgestellt und speziell für die mit Xcode 11 gelieferte Swift-Version implementiert wurde, sodass sie in SwiftUI verwendet werden kann.
Durch Markieren eines Typs @_functionBuilderkann er als benutzerdefiniertes Attribut für verschiedene Deklarationen wie Funktionen, berechnete Eigenschaften und in diesem Fall Parameter des Funktionstyps verwendet werden. Solche mit Anmerkungen versehenen Deklarationen verwenden den Funktionsgenerator, um Codeblöcke zu transformieren:
- Bei kommentierten Funktionen ist der Codeblock, der transformiert wird, die Implementierung.
- Bei kommentierten berechneten Eigenschaften ist der zu transformierende Codeblock der Getter.
- Bei mit Anmerkungen versehenen Parametern vom Funktionstyp ist der zu transformierende Codeblock ein beliebiger Abschlussausdruck, der an ihn übergeben wird (falls vorhanden).
Die Art und Weise, wie ein Funktions-Builder Code transformiert, wird durch die Implementierung von Builder-Methoden definiert, z. B. buildBlockdie eine Reihe von Ausdrücken verwenden und sie zu einem einzigen Wert zusammenfassen.
Zum Beispiel ViewBuilderArbeitsgeräte buildBlockfür 1 bis 10 Viewentsprechen , Parameter, mehrere Ansichten zu einem einzigen Konsolidierung 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
}
Auf diese Weise kann eine Reihe von Ansichtsausdrücken innerhalb eines an den VStackInitialisierer übergebenen Abschlusses in einen Aufruf umgewandelt werden buildBlock, der dieselbe Anzahl von Argumenten benötigt. Zum Beispiel:
struct ContentView : View {
var body: some View {
VStack(alignment: .leading) {
Text("Hello, World")
Text("Hello World!")
}
}
}
verwandelt sich in einen Aufruf an buildBlock(_:_:):
struct ContentView : View {
var body: some View {
VStack(alignment: .leading) {
ViewBuilder.buildBlock(Text("Hello, World"), Text("Hello World!"))
}
}
}
was dazu führt, dass der undurchsichtige Ergebnistyp some View erfüllt wird durch TupleView<(Text, Text)>.
Sie werden feststellen, dass ViewBuildernur buildBlockbis zu 10 Parameter definiert werden. Wenn wir also versuchen, 11 Unteransichten zu definieren:
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!")
}
}
Wir erhalten einen Compilerfehler, da es keine Builder-Methode gibt, um diesen Codeblock zu verarbeiten (beachten Sie, dass die Fehlermeldungen nicht so hilfreich sind, da diese Funktion noch in Arbeit ist).
In Wirklichkeit glaube ich nicht, dass Menschen so oft auf diese Einschränkung stoßen werden, zum Beispiel wäre das obige Beispiel besser, wenn man stattdessen die ForEachAnsicht verwendet:
var body: some View {
VStack(alignment: .leading) {
ForEach(0 ..< 20) { i in
Text("Hello world \(i)")
}
}
}
Wenn Sie jedoch mehr als 10 statisch definierte Ansichten benötigen, können Sie diese Einschränkung mithilfe der GroupAnsicht leicht umgehen :
var body: some View {
VStack(alignment: .leading) {
Group {
Text("Hello world")
}
Group {
Text("Hello world")
}
}
ViewBuilder implementiert auch andere Funktionserstellungsmethoden wie:
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
}
Dies gibt ihm die Möglichkeit, if-Anweisungen zu verarbeiten:
var body: some View {
VStack(alignment: .leading) {
if .random() {
Text("Hello World!")
} else {
Text("Goodbye World!")
}
Text("Something else")
}
}
was verwandelt wird in:
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")
)
}
}
(Das Aussenden redundanter 1-Argument-Aufrufe ViewBuilder.buildBlockerfordert Klarheit).
@ViewBuilderdeveloper.apple.com/documentation/swiftui/viewbuilder .