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 ViewBuilder
Typ, 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 @_functionBuilder
Attribut 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 @_functionBuilder
kann 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. buildBlock
die eine Reihe von Ausdrücken verwenden und sie zu einem einzigen Wert zusammenfassen.
Zum Beispiel ViewBuilder
Arbeitsgeräte buildBlock
für 1 bis 10 View
entsprechen , 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 VStack
Initialisierer ü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 ViewBuilder
nur buildBlock
bis 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 ForEach
Ansicht 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 Group
Ansicht 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.buildBlock
erfordert Klarheit).
@ViewBuilder
developer.apple.com/documentation/swiftui/viewbuilder .