SwiftUIのVStackやHStackでは、下のようにTextを2行並べて書くだけでTextを縦並びにできます。
今回はこの仕組みについて調べてみました。
struct ContentView: View {
var body: some View {
VStack {
Text("AAA")
Text("BBB")
}
}
}
上記のコードですが、下のような記述と同等になります。
複数個並べられていたTextは、ViewBuilderのbuildBlockの引数として使われます。
struct ContentView: View {
var body: some View {
VStack {
ViewBuilder.buildBlock(
Text("AAA"),
Text("BBB")
)
}
}
}
ViewBuilderのbuildBlockは、以下のようなTupleViewを返すメソッドです。
渡されたViewを元にTupleViewを作って返します。
extension ViewBuilder {
public static func buildBlock<C0, C1>(_ c0: C0, _ c1: C1) -> TupleView<(C0, C1)> where C0 : View, C1 : View
}
まとめると、省略記法を使わずにVStackを使う場合は以下のような記述になります。
struct ContentView: View {
var body: some View {
VStack(content: { () -> TupleView<(Text, Text)> in
return ViewBuilder.buildBlock(
Text("AAA"),
Text("BBB")
)
})
}
}
続けて、ViewBuilderを省略できた理由を見ていきます。
省略できたのは、ViewBuilderの宣言についている@_functionBuilderというAttributesが関係しています。
@_functionBuilder public struct ViewBuilder {
}
@_functionBuilderを使ってStructを作ると以下の@MyStruct
のようにクロージャーに付けるAttributesが作られます。
func myFunc(@MyStruct closure: () -> Int) -> Int {
return closure()
}
@_functionBuilder struct MyStruct {
static func buildBlock(_ v1: Int) -> Int {
return v1
}
static func buildBlock(_ v1: Int, _ v2: Int) -> Int {
return v1 + v2
}
static func buildBlock(_ v1: Int, _ v2: Int, _ v3: Int) -> Int {
return v1 + v2 + v3
}
}
@MyStructを付けたクロージャーは、以下のような省略記法を使えます。
ViewBuilderもこの機能を使う事でViewBuilder.buildBlock
という呼び出しを省略していました。
myFunc {
1
2
}
myFunc {
MyStruct.buildBlock(1, 2)
}
func myFunc(@MyStruct closure: () -> Int) -> Int {
return closure()
}
余談ですが@_functionBuilderはclassにも使うことができます。
@_functionBuilder class MyClass {
}
以下のように値の数がbuildBlockの引数の数を超えるとエラーになります。
myFunc {
1
2
3
4
}
ViewBuilderは引数が最大10個なので、下のようにViewを11個渡すとエラーになります。
struct ContentView: View {
var body: some View {
VStack {
Text("1")
Text("2")
Text("3")
Text("4")
Text("5")
Text("6")
Text("7")
Text("8")
Text("9")
Text("10")
Text("11")
}
}
}
参考URL
SwiftUIの魔法を実現する仕組み (Custom Attributes, Function Builder) - Qiita