[解決済み] SwiftUIのDSLを可能にするものは何ですか?
質問
Appleの新しい
SwiftUI
フレームワークでは
新しい種類の構文
を使い、効果的にタプルを構築しますが、別の構文があります。
var body: some View {
VStack(alignment: .leading) {
Text("Hello, World") // No comma, no separator ?!
Text("Hello World!")
}
}
この構文が実際にどのようなものであるかに挑戦しています。
であることがわかりました。
VStack
という型のクロージャを取ることがわかりました。
() -> Content
を第二引数として受け取ります。
Content
に準拠した一般的なパラメータです。
View
に準拠した一般的なパラメータで、クロージャを介して推論されます。どのような型であるのかを知るには
Content
がどのような型に推論されるかを知るために、その機能を維持したまま、コードを少し変更しました。
var body: some View {
let test = VStack(alignment: .leading) {
Text("Hello, World")
Text("Hello World!")
}
return test
}
これを使って
test
の型であることがわかります。
VStack<TupleView<(Text, Text)>>
であることを意味します。
Content
は
TupleView<Text, Text>
. 上を見ると
TupleView
から派生したラッパー型であることがわかりました。
SwiftUI
に由来するラッパー型であり、ラップすべきタプルを渡すことによってのみ初期化することができます。
質問
今、私は一体どうやって2つの
Text
のインスタンスがどのようにして
TupleView<(Text, Text)>
. にハックされているのでしょうか?
SwiftUI
であり、したがって
は無効な正規の Swift 構文ですか?
TupleView
であること
SwiftUI
タイプであることは、この仮定をサポートします。それとも、この
はSwiftの有効な構文でしょうか?
もしそうなら、どのようにして
の外でそれを使うことができますか?
SwiftUI
?
どのように解決するのですか?
マーティンが言うように
のドキュメントを見ると
VStack
's
init(alignment:spacing:content:)
を見ると
content:
パラメータには
@ViewBuilder
:
init(alignment: HorizontalAlignment = .center, spacing: Length? = nil,
@ViewBuilder content: () -> Content)
この属性は
ViewBuilder
型であり、生成されたインターフェイスを見ると、次のようになります。
@_functionBuilder public struct ViewBuilder {
/// Builds an empty view from an block containing no statements, `{ }`.
public static func buildBlock() -> EmptyView
/// Passes a single view written as a child view (e..g, `{ Text("Hello") }`)
/// through unmodified.
public static func buildBlock(_ content: Content) -> Content
where Content : View
}
は
@_functionBuilder
属性は、" と呼ばれる非公式な機能の一部です。
機能ビルダー
と呼ばれる非公式な機能の一部で、これは
は、Swift の進化に投じられたものです。
そして、Xcode 11に同梱されているSwiftのバージョンのために特別に実装され、SwiftUIで使用できるようになりました。
型をマークする
@_functionBuilder
をつけると、関数や計算されたプロパティ、この場合は関数型のパラメータなど、さまざまな宣言でカスタム属性として使うことができます。このような注釈付きの宣言は、コードのブロックを変換するために関数ビルダを使用します。
- アノテーションされた関数の場合、変換されるコードのブロックは実装です。
- アノテーションされた計算されたプロパティのために、変換されるコードのブロックはゲッターです。
- 関数型のアノテーションされたパラメータでは、変換されるコードのブロックは、それに渡される任意のクロージャ式です(もしあれば)。
ファンクションビルダがコードを変換する方法は、その実装によって定義されます。
ビルダーメソッド
といった
buildBlock
のように、一連の式を受け取り、それらを一つの値に統合するものです。
例えば
ViewBuilder
は
buildBlock
1から10まで
View
準拠のパラメータを使用し、複数のビューを1つの
TupleView
:
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension ViewBuilder {
/// Passes a single view written as a child view (e..g, `{ Text("Hello") }`)
/// through unmodified.
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
// ...
}
に渡されるクロージャの中で、ビュー式のセットを使用することができます。
VStack
のイニシャライザに渡されたクロージャ内のビュー式の集合を
buildBlock
の呼び出しに変換されます。例えば
struct ContentView : View {
var body: some View {
VStack(alignment: .leading) {
Text("Hello, World")
Text("Hello World!")
}
}
}
への呼び出しに変換されます。
buildBlock(_:_:)
:
struct ContentView : View {
var body: some View {
VStack(alignment: .leading) {
ViewBuilder.buildBlock(Text("Hello, World"), Text("Hello World!"))
}
}
}
の結果
不透明な結果型
some View
によって満たされる
TupleView<(Text, Text)>
.
ここで注目すべきは
ViewBuilder
のみを定義していることに注意してください。
buildBlock
は 10 個までのパラメータしか定義できないので、11 個のサブビューを定義しようとすると
var body: some View {
// error: Static member 'leading' cannot be used on instance of
// type 'HorizontalAlignment'
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!")
}
}
このコードブロックを処理するビルダーメソッドがないため、コンパイラーエラーが発生します (この機能はまだ作業中なので、エラーメッセージはそれほど役に立たないことに注意してください)。
実際には、この制限に遭遇することはそれほど多くないと思います。例えば、上記の例では
ForEach
を表示します。
var body: some View {
VStack(alignment: .leading) {
ForEach(0 ..< 20) { i in
Text("Hello world \(i)")
}
}
}
しかし、10 個以上の静的ビューが必要な場合、この制限を簡単に回避するために
Group
ビューを使用することで、この制限を簡単に回避することができます。
var body: some View {
VStack(alignment: .leading) {
Group {
Text("Hello world")
// ...
// up to 10 views
}
Group {
Text("Hello world")
// ...
// up to 10 more views
}
// ...
}
ViewBuilder
のような他のファンクションビルダのメソッドも実装しています。
extension ViewBuilder {
/// Provides support for "if" statements in multi-statement closures, producing
/// ConditionalContent for the "then" branch.
public static func buildEither<TrueContent, FalseContent>(first: TrueContent)
-> ConditionalContent<TrueContent, FalseContent>
where TrueContent : View, FalseContent : View
/// Provides support for "if-else" statements in multi-statement closures,
/// producing ConditionalContent for the "else" branch.
public static func buildEither<TrueContent, FalseContent>(second: FalseContent)
-> ConditionalContent<TrueContent, FalseContent>
where TrueContent : View, FalseContent : View
}
これにより、if文を扱えるようになります。
var body: some View {
VStack(alignment: .leading) {
if .random() {
Text("Hello World!")
} else {
Text("Goodbye World!")
}
Text("Something else")
}
}
に変換される。
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")
)
}
}
(への冗長な1引数呼び出しが発生します。
ViewBuilder.buildBlock
への冗長な1引数呼び出しを行なっています)。
関連
-
[解決済み] Protocol ... can only be used as generic constraint because it has Self or associated type requirements "とは、どういう意味ですか?
-
[解決済み] swiftで改行なしの印刷をする
-
[解決済み] クラス 'ViewController' は swift で初期化されません。
-
[解決済み] UIViewの定高制約をプログラムで更新するには?
-
[解決済み] 既存のUIKitアプリケーションにSwiftUIビューを含める
-
[解決済み] Swiftで複数のカスタムセルを持つUITableview
-
[解決済み] Swift: インデックスで文字列配列を置換する
-
[解決済み] ボタンを無効化する
-
[解決済み] SwiftUIのForEachでインデックスを取得する
-
[解決済み] SwiftでNSDatesの秒数の差を整数値で求める。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
swift 4.0でのdispatch_async,dispatch_afterの使用について
-
[解決済み】Swift(UI)の "some "キーワードとは?)
-
[解決済み] Swiftで複数のカスタムセルを持つUITableview
-
[解決済み] Swift: PREPROCESSORフラグ(`#if DEBUG`など)を使ってAPIキーを実装する方法とは?
-
[解決済み] swiftにおける "precondition "と "assert "の違いとは?
-
[解決済み] Swift - サブクラスでオーバーライドする必要があるクラスメソッド
-
[解決済み] SwiftUIで条件付きでビューを使用する
-
[解決済み] Swiftの変数に"?"(クエスチョンマーク)と"!"(エクスクラメーションマーク)の装飾を施す。
-
[解決済み] Swift の @escaping と Completion ハンドラ
-
[解決済み] Xcode 8.3 betaの「String interpolation produces a debug description for an optional value; did you mean to make this explicit?