WWDC Quick Look 💓 By SwiftGGTeam
The craft of SwiftUI API design: Progressive disclosure

The craft of SwiftUI API design: Progressive disclosure

元の動画を見る

ハイライト

SwiftUI チームの Sam Lazarus は、macOS の保存ダイアログという賢い角度からこの問題に取り組んでいます。シンプル モードでは、ファイル名と一般的に使用されるいくつかの場所のみが表示され、拡張すると完全なファイル ブラウザーになります。さまざまなレベルの複雑さの要件には、さまざまなレベルの複雑さの UI が表示されます。これが漸進的開示です。


主要内容

このセッションでは、SwiftUI チームが API を設計する方法について説明します。

Sam Lazarus による定義は非常に単純です。漸進的開示とは、使用シナリオの複雑さに応じて通話サイトの複雑さを増加させることです (02:19)。単純なシナリオの場合は、必要な情報のみを記述します。複雑なシーンでは、さらに多くの機能が可能になります。

これは、macOS の保存ダイアログに似ています。デフォルト状態では、保存場所と共通の場所が指定されています。ファイル システムを参照する必要がある場合、ユーザーは完全なインターフェイスを展開できます (01:14)。 SwiftUI は、この種のエクスペリエンスを API に組み込みたいと考えています。初めて使用するときはすぐに使用でき、その後の拡張には制限がありません。

Sam 氏は、再利用可能なコンポーネントや抽象化はすべて API であると強調しました。コンポーネントを作成する人は通常、宣言サイトを見ますが、ユーザー エクスペリエンスは呼び出しサイト (02:00) で発生します。これは、この分野のメインスレッドでもあり、呼び出しポイントから API の形状を推定します。

詳細

一般的なユースケースから始める

(04:18) SwiftUIButtonが最初の例です。ボタンにはラベルが必要です。ほとんどのボタンのラベルは単なるテキストであるため、最も一般的な方法は短く書くことです。

Button("Next Page") {
    currentPage += 1
}

キーポイント:

  • Button("Next Page")最も一般的なテキスト ラベルを第 1 レベルの API に組み込みます。
  • ボタンのアクションのみがクロージャーに保持されます。これは、現在のページを 1 つ増やすことを目的としています。
  • コールポイントが表示されないTextHStackこのシナリオでは必要ないため、追加のパラメーターを追加します。

ラベルをカスタマイズする必要がある場合、SwiftUI はフルバージョン (04:36) を提供します。

Button {
    currentPage += 1
} label: {
    Text("Next Page")
}

キーポイント:

  • 最初のクロージャーは依然としてボタンアクションです。 -labelクロージャはラベルのビュー構造を公開し始めます。 -Text("Next Page")呼び出し元が通常の SwiftUI ビュー機能を引き続き使用できるようにします。

より複雑なラベルは同じエントリを引き続き使用します (04:43)。

Button {
    currentPage += 1
} label: {
    HStack {
        Text("Next Page")
        NextPagePreview()
    }
}

キーポイント:

  • HStackテキストとプレビュー ビューをラベルに結合します。 -Text("Next Page")まだ読みやすい名前を想定しています。 -NextPagePreview()必要な場合にのみ表示される追加のカスタム コンテンツです。

これは、漸進的開示の最初の戦略です。最初に最も一般的な使用例を特定し、次にそれらの使用例に対する簡潔なエントリ ポイントを提供します。複雑な機能は次のレベルの API に残ります。

一般的なシナリオ向けにスマートなデフォルトを設定する

05:30Textはスマートなデフォルトの例です。

Text("Hello WWDC22!")

キーポイント:

  • 呼び出しサイトは表示されるものだけを書き込みます。
  • SwiftUI は、環境内のロケールを使用して、アプリ バンドル内のローカライズされた文字列を検索します。
  • テキストは現在の配色に自動的に適応し、ダーク モードをサポートします。
  • テキストは、現在のアクセシビリティのダイナミック タイプの寸法に基づいて自動的に拡大縮小されます。

これらの動作は手動で指定できます。ただし、通常のシナリオでは、コール ポイントを占有するべきではありません。

Textもう 1 つ詳細があります。二Textスタックに配置されると、テキスト間の間隔は、現在のコンテキスト (06:12) の正しい行間隔に自動的に調整されます。

VStack {
    Text("Hello WWDC22!")
    Text("Call to Code.")
}

キーポイント:

  • VStack2つのテキストの縦方向の配置のみを表現します。
  • それぞれTextまだ内容を宣言するだけです。
  • 行間隔などのタイポグラフィの詳細は、SwiftUI のデフォルトによって処理されます。

ツールバーも同じ考え方を使用しています。位置が指定されていない場合、SwiftUI はプラットフォームの規則に従ってボタンを配置します。macOS はボタンをツールバーの先端に配置し、iOS はナビゲーション バーの後端に配置します。watchOS は最初の項目のみを表示し、ナビゲーション バーの下に固定します (06:43)。位置を制御する必要がある場合は、配置を使用しますToolbarItemGroup07:20)。

最適化の呼び出しポイント: Table の 4 つの短縮形

08:09Tableこれは、この分野で最も完全な例です。複数列テーブルは、並べ替え、リッチ コンテンツ セル、行のグループ化などの機能をサポートできます。完全な書き込みは非常に強力ですが、通常のフォームでは毎回完全な構造を書き込む必要はありません。

複雑なバージョンは次のようになります。

@State var sortOrder = [KeyPathComparator(\Book.title)]

var body: some View {
    Table(sortOrder: $sortOrder) {
        TableColumn("Title", value: \Book.title) { book in
            Text(book.title).bold()
        }
        TableColumn("Author", value: \Book.author) { book in
            Text(book.author).italic()
        }
    } rows: {
        Section("Favorites") {
            ForEach(favorites) { book in
                TableRow(book)
            }
        }
        Section("Currently Reading") {
            ForEach(currentlyReading) { book in
                TableRow(book)
            }
        }
    }
    .onChange(of: sortOrder) { newValue in
        favorites.sort(using: newValue)
        currentlyReading.sort(using: newValue)
    }
}

キーポイント:

  • sortOrder現在の並べ替えルールを保存します。 -Table(sortOrder:)列ヘッダーのクリックに基づいてテーブルの並べ替え状態を変更できるようにします。 -TableColumn列名、値キーのパス、セルビューを宣言します。 -Section行を異なる領域に分割します。 -ForEachコレクション内の各書籍を次のように変換しますTableRow
  • .onChange(of: sortOrder)変更を並べ替えるときにデータ ソースの順序を変更します。

このバージョンは複雑なフォームに適しています。次に、SwiftUI は、通常のシナリオでは必要のない情報を徐々に削除します。

最初のステップでは、多くの場合、コレクションに対して通常の行が実行されます。ForEach、要素ごとに生成しますTableRow。 SwiftUI を使用すると、コレクションを直接渡すことができます。Table09:58)。

@State var sortOrder = [KeyPathComparator(\Book.title)]

var body: some View {
    Table(currentlyReading, sortOrder: $sortOrder) {
        TableColumn("Title", value: \.title) { book in
            Text(book.title)
        }
        TableColumn("Author", value: \.author) { book in
            Text(book.author)
        }
    }
    .onChange(of: sortOrder) { newValue in
        currentlyReading.sort(using: newValue)
    }
}

キーポイント:

  • Table(currentlyReading, sortOrder:)データ収集をテーブルに渡します。
  • コールポイントは手書きではなくなりましたForEachそしてTableRow
  • \.title\.authorコレクション要素の型はすでに次のように表されているため、相対キー パスを使用します。currentlyReadingロールアウトする。
  • このバージョンでも並べ替えがサポートされているため、並べ替えロジックは引き続き保持されます。

2 番目のステップでは、列の値キー パスが文字列を指している場合、最も一般的な表示方法は次のとおりです。Text。 SwiftUI ではセル ビューを省略できます (10:23)。

@State var sortOrder = [KeyPathComparator(\Book.title)]

var body: some View {
    Table(currentlyReading, sortOrder: $sortOrder) {
        TableColumn("Title", value: \.title)
        TableColumn("Author", value: \.author)
    }
    .onChange(of: sortOrder) { newValue in
        currentlyReading.sort(using: newValue)
    }
}

キーポイント:

  • TableColumn("Title", value: \.title)タイトル列を生成するだけで十分です。 -TableColumn("Author", value: \.author)著者列も同様に生成されます。
  • 独房の中Text(book.title)そしてText(book.author)デフォルトの動作でオーバーライドされます。
  • ソート バインディングとソート コールバックは引き続き呼び出しサイトに表示されます。

3 番目のステップでは、最も単純なテーブルはソートを考慮しません。 SwiftUI は、ソート順なしのバージョン (10:51) を提供するようになりました。

var body: some View {
    Table(currentlyReading) {
        TableColumn("Title", value: \.title)
        TableColumn("Author", value: \.author)
    }
}

キーポイント:

  • Table(currentlyReading)データソースを表します。
  • TableColumn表示する必要がある列を示します。
  • いいえsortOrderこのユースケースでは並べ替えは必要ないためです。
  • このテーブルを表現するにはデフォルトの動作で十分であるため、行のクロージャとセルのクロージャはありません。

Sam は 2 つの検査上の質問を要約しました。どの一般的なユースケースが利便性に値するのか。どの情報は常に提供する必要がある必須情報です (11:03)。 Table の最終形式は、これら 2 つの質問の結果です。

すべての可能性を列挙するのではなく、組み合わせて使用​​します。

(11:37) 最後の戦略は Compose です。列挙しないでください。

HStack には、コンテンツとは何か、およびコンテンツがどのように配置されているかという 2 種類のコア情報があります。内容はビュービルダーによって表現されています。 enum を使用して配置が設計されている場合、すぐに制御不能になります。先頭、中央、末尾の後は、要素間のスペースのみを残し、最後の要素の前のスペースのみを残すなど、均等な分布も存在します (12:08)。

各順列に列挙型ケースを追加する代わりに、SwiftUI は以下を提供します。Spacer、呼び出し元が順列を組み立てることができます。

struct StackExample: View {
    var body: some View {
        HStack { // centered
            Spacer()
            Box().tint(.red)
            Box().tint(.green)
            Box().tint(.blue)
            Spacer()
        }
    }
}

キーポイント:

  • HStackまだコンテンツを水平方向に配置することのみを担当します。
  • 初めSpacer()ボックス全体を中央に向かって押します。
  • 三つBox().tint(...)実際の内容です。
  • 最終Spacer()最初のスペーサーと組み合わせると、中央に配置された効果が得られます。

均一な配布には新しい API は必要ありません (13:42)。

struct StackExample: View {
    var body: some View {
        HStack { // evenly spaced
            Spacer()
            Box().tint(.red)
            Spacer()
            Box().tint(.green)
            Spacer()
            Box().tint(.blue)
            Spacer()
        }
    }
}

キーポイント:

  • それぞれSpacer()これらはすべて、構成可能なレイアウト要素です。
  • コンテンツ間にスペーサーが挿入されると、空白が複数の場所に広がります。 ・APIは成長していないが、表現力は拡張されている。

要素間にスペースを残す場合も同様です (13:43)。

struct StackExample: View {
    var body: some View {
        HStack { // space only between elements
            Box().tint(.red)
            Spacer()
            Box().tint(.green)
            Spacer()
            Box().tint(.blue)
        }
    }
}

キーポイント:

  • Box().tint(.red)Box().tint(.green)Box().tint(.blue)は 3 つのコンテンツ ビューです。
  • Spacer()コンテンツ間のみに表示されます。
  • 先頭と末尾にスペーサーがないため、両端に空白が入りません。

注意事項のこの部分は非常に実践的です。「一般的な状況」を「すべての一般的な状況を列挙として書く」と誤解しないでください。可能性が広がり続ける場合は、小さな構成可能な単位に分割する必要があります (13:20)。


重要ポイント

  • やるべきこと: チーム内の SwiftUI コンポーネントの最短の呼び出しエントリを作成します。 実行する価値がある理由: セッションは、コール ポイントの複雑さが使用シナリオに一致する必要があることを繰り返し強調しています。一般的なシナリオを最初に実行する必要があります。 開始方法: コンポーネントの 80% がどのように呼び出されるかを確認し、必要なパラメーターのみを含むイニシャライザーをそれに追加し、高度な構成は完全なイニシャライザーに任せます。

  • 内容: カスタム ビュー パラメーターのスマートなデフォルト値を設定します。 実行する価値がある理由:Textおよびツールバーの例は、デフォルトによって、ローカリゼーション、カラースキーム、プラットフォーム規則などの詳細が通常の呼び出しポイントから遠ざかる可能性があることを示しています。 開始方法: 各パラメータを毎回書き込む必要があるかどうかを確認します。省略できるパラメータについては、デフォルトのパラメータまたはデフォルトのパラメータに変更します。Environment読み込んでください。

  • 内容: テーブルまたはリスト コンポーネントの呼び出しサイトをオーバーライドします。 実行する価値がある理由: 表の例は、全機能から単純な入力への縮小プロセスを示しています。 開始方法: まず完全なバージョンを保持し、次にコレクション入力、文字列列、および並べ替えられていないシナリオ用の便利な API を追加します。

  • 内容: enum 設定をコンポーザブル ビューまたはモディファイアに分割します。 実行する価値がある理由: HStack はすべての配置を列挙するわけではありませんが、Spacer開発者がレイアウトを作成できるようにします。 開始方法: enum がケースを追加し続ける場合は、まずこれらのケースをより小さな構造から結合できるかどうかを確認します。

  • やるべきこと: API を作成する前に呼び出しの例を作成します。 実行する価値がある理由: この分野の中心的な視点は呼び出しサイトであり、API が使用時点からスムーズであるかどうかを確認することが優先されます。 開始方法: まず、最も一般的なコード、少し複雑なコード、そして最も高度なコードの 3 つの部分のターゲット呼び出しコードを作成します。これらの呼び出しポイントに基づいて、型、初期化子、およびデフォルト値が推定されます。


関連セッション

コメント

GitHub Issues · utterances