WWDC Quick Look 💓 By SwiftGGTeam
SwiftUI on iPad: Organize your interface

SwiftUI on iPad: Organize your interface

元の動画を見る

ハイライト

セッションは、リストとテーブル、選択モデル、列ビューの 3 つのテーマを中心に構成されます。


主要内容

iPhone のリストを iPad に移動するときに最もよくある問題は、リストが空であることです。画面は大きくなりましたが、情報密度は変わりません。ユーザーには依然として 1 列のコンテンツしか表示されず、クリックしてクリックアウトする必要があります。

このセッションでは、例として「静かな読書の場所」アプリを使用します。元のインターフェイスはList, iPadのスペースの無駄です。 iPadOS 16 では、SwiftUI の複数列テーブル (Table) が iPad に導入され、同じ API が macOS でも利用可能です。リストは表に変更でき、名前、快適さ、騒音レベルをそれぞれ列に入れて、ユーザーが直接並べ替えることができます。

フォームは最初のステップにすぎません。生産性向上アプリには、選択、バッチ操作、コンテキスト メニューも必要です。 SwiftUI の選択モデルはタグと選択状態を使用し、Table は行値の識別子を選択タグとして自動的に使用します。 iPadOS 16 では軽量の複数選択も追加されています。キーボードを接続した後、ユーザーは編集モードに入らずに Shift と Command を使用して選択範囲を拡張できます。

最後に構造があります。多くの iPad アプリは依然としてポップアップ ウィンドウとレイヤーごとのジャンプを使用してメイン プロセスをホストしています。 iPadOS 16 および macOS Ventura で強化された SwiftUINavigationSplitViewは、2 列、3 列、およびさまざまな分割ビュー スタイルをサポートします。サイドバー、コンテンツ、詳細を横画面、縦画面、Slide Over、コンパクト サイズ クラスで自動的に調整できるため、モーダル インターフェイスの使用を削減できます。

詳細

リストからテーブルへの移行

(03:10) この例は通常のリストから始まります。データを表示できますが、使用する列は 1 つだけです。

struct PlacesList: View {
    @Binding var modelData: ModelData

    var body: some View {
        List(modelData.places) { place in
            PlaceCell(place)
        }
    }
}

キーポイント:

  • @Binding var modelData: リストは外部受信データ ソースを直接使用します。 -List(modelData.places): それぞれPlace行を生成します。 -PlaceCell(place): 元のセル ビューは引き続き再利用できます。

(03:18) テーブルの構成は異なります。受け取るのは列ビルダーです。最初の列は引き続き再利用できますPlaceCell、次の列では使用しますTableColumn他のフィールドを表示します。

struct PlacesTable: View {
    @Binding var modelData: ModelData
    @State private var sortOrder = [KeyPathComparator(\Place.name)]

    var body: some View {
        Table(modelData.places, sortOrder: $sortOrder) {
            TableColumn("Name", value: \.name) { place in
                PlaceCell(place)
            }
            TableColumn("Comfort Level", value: \.comfortDescription).width(200)
            TableColumn("Noise", value: \.noiseLevel) { place in
                NoiseLevelView(level: place.noiseLevel)
            }
        }
        .onChange(of: sortOrder) {
            modelData.sort(using: $0)
        }
    }
}

キーポイント:

  • @State private var sortOrder: 現在の並べ替えコンパレーターを、最初は名前で並べ替えて保存します。 -Table(modelData.places, sortOrder: $sortOrder): データと並べ替えステータスをテーブルにバインドします。 -TableColumn("Name", value: \.name): 列名はヘッダーに使用され、キーパスは並べ替えに使用されます。 -PlaceCell(place): 最初の列には、コンパクトなレイアウトに必要な完全な情報が保持されます。 -TableColumn("Comfort Level", value: \.comfortDescription).width(200): 文字列列はビュー ビルダーを省略して固定幅を設定できます。 -TableColumn("Noise", value: \.noiseLevel): 非プレーン テキスト コンテンツでは引き続きビュー ビルダーが使用されます。 -.onChange(of: sortOrder): テーブルはデータを再配置しません。データ ソースの並べ替えは自分で実行する必要があります。

ここには iPad 固有の制限が 2 つあります。まず、コンパクト サイズ クラスのテーブルでは最初の列のみが表示されるため、最初の列は行の内容を独立して表現できる必要があります。次に、iPad の表は水平方向にスクロールしないため、列の数を制御する必要があります。そうしないと、情報が密集してしまいます。

選択モデル: タグと選択状態

(06:22) SwiftUI の選択モデルは、各行のタグと選択された項目を保持する状態の 2 つの部分で構成されます。複数の選択を行う場合、状態は通常、Set、選択した行のタグが保存されます。

(07:45) タグとアイデンティティは似ていますが、同じではありません。ForEachタグは明示的な ID から自動的に派生します。テーブルは行値の識別子を選択タグとして使用します。手動でタグを設定する場合は、tag修飾子、値は次のようにする必要がありますHashable、同じ選択可能なコンテナ内のタグ タイプは一貫している必要があります。

(10:25) 複数の選択をテーブルに追加するには、選択状態を追加し、バインディングをテーブルに渡すだけです。

struct PlacesTable: View {
    @EnvironmentObject var modelData: ModelData
    @State private var sortOrder = [KeyPathComparator(\Place.name)]
    @State private var selection: Set<Place.ID> = []

    var body: some View {
        Table(modelData.places, selection: $selection, sortOrder: $sortOrder) {
            // columns
        }
    }
}

キーポイント:

  • @EnvironmentObject var modelData:Table は環境から共有モデルを読み取ります。 -@State private var sortOrder:ソート状態が保持されます。 -@State private var selection: Set<Place.ID>:複数選択状態で使用Set複数の行識別子を保存します。 -selection: $selection: テーブルはバインディングを通じて現在の選択内容の読み取りと書き込みを行います。 -Set<Place.ID>: タイプはテーブルの行識別子と一致する必要があります。 -// columns: 列の定義は、以前の TableColumn の記述方法に従うことができます。

(09:06) iPadOS 16 では軽量の複数選択が追加されています。キーボードを接続した後、ユーザーは編集モードに入らずに Shift と Command を使用して複数選択できます。タッチ シーンには引き続き編集モードが必要で、システムは複数選択ジェスチャを入力するための 2 本指のパンを自動的にサポートします。

ツールバーを使用して選択操作を公開する

(10:56) 選択自体には値はなく、バッチ アクションのみが値を持ちます。例: 「選択した場所をガイドに追加」ボタンをツールバーに追加し、そのままにしておくEditButtonキーボードを持たないユーザーが編集モードに入ることができます。

Table(modelData.places, selection: $selection, sortOrder: $sortOrder) {
    ...
}
.toolbar {
    ToolbarItemGroup(placement: .navigationBarTrailing) {
        if !selection.isEmpty {
            AddToGuideButton(selection)
        }
    }

    ToolbarItemGroup(placement: .navigationBarLeading) {
        EditButton()
    }
}

キーポイント:

  • selection: $selection: ツールバーの表示ロジックは同じ選択状態に依存します。 -if !selection.isEmpty:項目が選択されていない場合、一括操作ボタンは表示されません。 -AddToGuideButton(selection):アクションは、現在選択されている識別子のセットを受け取ります。 -EditButton(): キーボードがない場合でも、ユーザーは編集モードに出入りするための明確な入り口を持っています。

この設計は、両方のタイプの iPad の使用法に対応します。キーボードとトラックパッドのユーザーは直接複数選択できます。 Pure Touch ユーザーは、編集モードを通じて同じ操作を完了できます。

###複数選択コンテ​​キスト メニュー

(11:41) SwiftUI は、iOS 16、iPadOS 16、macOS Ventura に複数選択のコンテキスト メニューを追加します。メニュー クロージャは、選択された識別子の配列を受け取ります。コレクションが空の場合は、ユーザーが空白の領域でメニューを開くことを意味します。コレクションに要素が 1 つしかない場合、それは 1 行のメニューを意味します。コレクション内に複数の要素がある場合、それは複数選択メニューを意味します。

// Item context menus

Table(modelData.places, selection: $selection, sortOrder: $sortOrder) {
    ...
}
.contextMenu(forSelectionType: Place.ID.self) { items in
    if items.isEmpty {
        // Empty area
        AddPlaceButton()
    } else {
        if items.count == 1 {
            // Single item
            FavoriteButton(isSet: $modelData.places[items.first!].isFavorite)
        }

        // Single and multiple items
        AddToGuideButton(items)
    }
}

キーポイント:

  • contextMenu(forSelectionType: Place.ID.self): 選択タイプはテーブルの選択タイプと一致している必要があります。 -{ items in ... }: クロージャは、このメニューで使用される識別子のセットを取得します。 -if items.isEmpty: 空白領域メニューは、「新しい場所を追加する」などのグローバル アクションに適しています。 -if items.count == 1: 単一項目メニューには、単一のオブジェクトにのみ適用されるお気に入りなどのアクションを含めることができます。 -AddToGuideButton(items): 単一選択と複数選択の両方でバッチ アクションを再利用できます。

空のコレクション ブランチがビューを生成しない場合、SwiftUI は空の領域にメニューを表示しません。これにより、空白メニューをオンデマンドで開くことができます。

(13:52) iPad の大画面は、より多くのレベルを同時に表示するのに適しています。 SwiftUI は、iPadOS 16 および macOS Ventura での分割ビューのサポートを改善し、追加しましたNavigationSplitView、2 列と 3 列をサポートします。

2 列レイアウトでは、先頭の列はサイドバーと呼ばれ、後続の列は詳細と呼ばれます。デフォルトでは、横画面が横に並んで表示されます。画面が縦向きの場合、サイドバーは引っ込められ、詳細が表示されたままになります。prominentDetailより詳細にこだわるようになるので、balanced2 つの列の重みのバランスがとれます。

3 列レイアウトでは、コンテンツ列が追加されます。コンテンツと詳細はデフォルトで横向きモードで表示され、サイドバーは切り替えることができます。縦画面では、最初に詳細が表示され、ボタンを使用してコンテンツとサイドバーが順に展開されます。セッションでは、利用可能なスペースに応じて適応されるため、3 列レイアウトの場合は最初に自動スタイルを使用することをお勧めします。

(16:55) 最小の 2 列分割ビュー コードは次のとおりです。

// Navigation Split View example

struct ContentView: View {
    var body: some View {
        NavigationSplitView {
            SidebarView()
        } detail: {
            Text("Select a place")
        }
    }
}

キーポイント:

  • NavigationSplitView: SwiftUI 分割ビューを宣言します。 -SidebarView(): 最初のクロージャーはサイドバー列です。 -detail:: 2 番目のクロージャーは詳細列です。 -Text("Select a place"): サイドバーにコンテンツが表示されない場合は、プレースホルダー ビューで詳細を入力します。

(17:24) Slide Over またはコンパクト サイズ クラスに入ると、列は自動的にスタックに折りたたまれます。このようにして、同じ構造で iPad の横長、縦長、狭いウィンドウをカバーできます。

重要ポイント

  • 対処方法: データベース、タスク データベース、および場所データベースを単一列リストからテーブルに変更します。 実行する価値がある理由: iPadOS 16 の SwiftUI Table は、複数の列、並べ替え、セクションをサポートしており、情報密度を高めることができます。 開始方法: まず元のセルを最初の列として保持し、次に使用しますTableColumn並べ替え可能なフィールドを追加し、onChange(of: sortOrder)のデータ ソースを再配置します。

  • 何をすべきか: バッチ並べ替えシナリオに軽量の複数選択を追加します。 実行する価値がある理由: キーボード ユーザーは Shift と Command を直接使用して複数選択でき、タッチ ユーザーは編集モードで同じプロセスを完了できます。 開始方法: テーブルに追加@State private var selection: Set<Model.ID>、バンドル$selectionに渡すTable、次に、に従ってツールバーにselection.isEmptyバッチボタンを表示します。

  • 作業内容: 空白領域、単一項目、および複数項目のコンテキスト メニューをデザインします。 実行する価値がある理由: 複数選択のコンテキスト メニューを使用すると、アクションをユーザーの現在の選択に近づけることができるため、ツールバーを行ったり来たりする回数が減ります。 開始方法: を使用しますcontextMenu(forSelectionType:)、によるとクロージャー内items.isEmptyそしてitems.countメニュー内容を選択します。

  • 対処方法: メインプロセスをポップアップウィンドウからサイドバー+詳細に変更します。 実行する価値がある理由:NavigationSplitView横画面ではより多くの情報を表示でき、縦画面やSlide Overでは自動的に折りたたまれます。サイズごとに個別の構造体を記述する必要はありません。 開始方法: 最初に 2 つの列を構築しますNavigationSplitView、コレクションのエントリをサイドバーに配置し、現在のコンテンツと空の状態を詳細に配置します。

関連セッション

コメント

GitHub Issues · utterances