ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
iPadのSwiftUI:インターフェイスをオーガナイズする
SwiftUIのリストと表を使用して、iPad Appのインターフェイスを強化しましょう。ここでは、選択インタラクションとコンテキストメニューを追加して、Appの生産性を高める方法を紹介します。また、ナビゲーション構築のベストプラクティスを紹介し、Split Viewでモダリティを回避して、デスクトップクラス最高レベルのiPadエクスペリエンスを実現する方法について解説します。 これは、2部構成シリーズの前半セッションになります。この動画を最大限に活用するには、SwiftUIに関する基本的な知識を習得しておくとよいでしょう。本セッションの視聴後、「iPadのSwiftUI:ツールバーやタイトルなどを追加する」では、SwiftUIでiPad Appのツールバーをさらに改善する方法を紹介していますので、こちらもご覧ください。
リソース
関連ビデオ
WWDC22
- iPad Appデザインの最新情報
- iPadのSwiftUI:ツールバーやタイトルなどを追加する
- SwiftUI Appに複数のウインドウを追加する
- SwiftUIのナビゲーション機能
- SwiftUIの最新情報
- WWDC22 Day 4 recap
WWDC21
-
ダウンロード
♪ ♪
こんにちは ようこそ 「iPadのSwiftUI: インターフェイスを構造化する」へ 私はRajです SwiftUIに携わっています iPadOS 16は より生産的で プロフェッショナルなAppを構築できるよう 多数のアップデートを提供します このセッションではiPad上でSwiftUI Appの インターフェイスを整理することを紹介します まず始めに リストとテーブルについてです それからSwiftUIの選択モデルと メニューと選択をまとめる方法について お話します 最後に Split Viewを使用して iPad用のAppのナビゲーションを 構成する方法について説明します ちょっと待ってくださいまだあります 実は これは2部構成のシリーズの1回目です 第2部で 同僚のHarryが ツールバー タイトルなどを紹介します Harryは SwiftUIのiPad Appを 次のレベルに引き上げるための 重要な追加機能をカバーしているので ぜひ両方のセッションをご覧ください まずは リストとテーブルから始めましょう 最近いくつかの読書会に参加しましたが 遅れ気味です 静かな場所で読書するのは難しいですから そこで 読書に集中するため 静かな場所を見つける Appを作り始めました オアシスのような静かな場所では 次々とページが進みます このAppは 見つけた場所を 記録するのに役立ちます iPhone用のAppですがiPad用にアップデートして より大きなディスプレイを 活用したら楽しいと思います iPad用のAppをより良いものにすることで いずれMacにも対応させたいと考えています このセッションではMacを 明確にカバーしませんが 紹介するAPIの多くはmacOSにも適用されます これは これまでに見つけた 静かな場所のリストです これはAppのアップデートを 始める最適な場所です iPad版の作成に取り掛かりました ひどいものではありませんが 大きな画面の活用がないです 無駄なスペースがかなりあり 情報密度も低いです ありがたいことにiPadOS 16では 情報密度の高いシナリオでは 複数列のテーブルという 素晴らしいソリューションがあります どのようなものかご紹介しましょう 複数列のテーブルのために SwiftUI APIを採用した後の 「All Places」のViewです このスクリーンショットを 次の数分間で作業します SwiftUIにおける複数列のテーブルは macOS Montereyで導入し iPadOS 16からiPadでもAPIが利用できます iPadのテーブルも複数列と 並べ替えをサポートします iPadでテーブルが導入され SwiftUIは iPadとMacで テーブのセクションをサポートしています
「MacにおけるSwiftUI :基礎の構築」は 一般的なガイダンスでiPadでも適用されます まだ見ていないのでしたら 見ることをお勧めします
iPhoneのリストから先ほどの表を作りましょう これが先程の場所のリストのコードです
まず リストからテーブルに切り替えてみます テーブルはリストの構造と異なります View Builderに代えて Column Builderがあります
最初に追加する列は地名です 列にはヘッダーの名前と コレクション内の各要素のViewを生成する View Builderが必要です 値のキーパスも指定しました これは後でテーブルに 並べ替えをするとき重要になります View builderはリストベースの 構築に似ています 実は以前のPlaceCell型を 再利用することもできます
Compact Size Classでは テーブルは最初の列しか表示されませんが iPhoneでもiPadのスライドオーバーでも 美しく表示されます
見た目がリストに似ていることに お気づきでしょう 単にテーブルをリストに 置き換えたのではありません テーブルを再利用すれば Size Class間の移行時に スクロール位置と選択範囲が 維持されるからです 一般的にコンパクトな外観を実現するために 最初の列が使用されることをご確認ください また iPad Appはスライドオーバーなどの 環境でもテストしてください
さて 次です 快適さと騒音レベルについて 列を追加していきます テキストコンテンツだけの 列では TableColumnは View Builderを省略する便利なAPIを提供します この場合「快適さ」のセルは あまり長さを必要としないでしょうから 固定幅を適用しています
コンパレータでテーブルを 並べ替えることもできます コンパレータを格納するステートを作ります ここでのステートは テーブルのすべてのコンパレータを表すので 配列です また 名前のコンパレーターに 初期値を設定することで 初回表示時にテーブルを 並べ替えて表示されます
次にバインディングを渡し関連づけます
列は比較可能なフィールドへ キーパスとして値を指定するので デフォルトで並べ替え可能です 表は 名前 快適度 騒音で 完全に並べ替えできます テーブルがそれ自体 並べ替えしないことに注意です それは私次第です onChange modifierを使用し データを入れ替えます
さあ ちょっとやってみましょう
テーブルは見やすく すべての場所のデータが表示され 大きな画面を活かしています Macと違って iPadのテーブルは 水平方向にスクロールしないので 列を制限することが重要です これで すべての列を一度に 表示することができます 各列のラベルはヘッダーに表示されます レベルをタップすると その列が並べ替えされます 騒音レベルで並び替えることもできます
スライドオーバーすると テーブルは列に折りたたまれ すべての情報がより 凝縮された形で表現されます リストをテーブルに更新し 選択について説明します このセクションで SwiftUIの 選択モデルをレビューし メニューを統合することを説明します その過程で豊富な機能で 場所のテーブルを強化します 最初にSwiftUIでどのように 動作するかお話します SwiftUIには選択を管理する 安定したAPIがあります ここに いくつかの行を含む リストの図があります それぞれの行はタグを持っています タグは各行に固有の値で リストの選択管理に有効です この図ではタグは緑色の円で表示されています
タグと一緒に選択項目を 保持している状態もあります タグの値を保持する型です 例えば 複数選択の場合 選択された各行のタグを保持するセットです リストの仕事は各行のタグと 選択状態の間を調整することです それは選択のバインディングによって 行われます 行が選択されると例えば 2番目の行が 選択のバインディングによって セットに追加されます 同様にAppの他の部分が プログラムによってセットを変更し ここに示す3つを追加すると 選択のバインディングが変更され リストがそれを選択します この一般的なモデルはiOSとmacOSで共通です つまり選択には2つの部分があります タグと状態です 次にタグの由来についてお話します タグは選択可能なコンテナのViewの値であり そのViewが選択されているか 追跡するために使用されます SwiftUIは自動的にタグを 合成することができます タグは識別子に似ていますが 全く同じではありません ForEachを使用するときSwiftUIはViewのタグを 明示的なIDから自動的に導き出します テーブルで行の値の識別子を 選択タグとして使用します このAppでは place構造体の 識別子型が使われます 明示的なIDの詳細については 「SwiftUIの徹底解説」をご覧ください
Viewに手動でタグを付ける場合 タグ修飾子を使います これはForEachが内部で行っていることです タグ修飾子はHashableの値をとります タグ修飾子を使用する場合は注意が必要です 選択可能なコンテナ内のすべてのViewが 同じタグタイプを共有することが重要です SwiftUIは Viewの選択方法を 知らないかもしれないです 注意点は IDモディファイア使用時は タグは設定しません 以上がタグの概要です 先ほどの図に戻しましょう
さて この図のタグの部分を説明したので ここでは選択式のもう半分である 「選択状態」に着目してみたいと思います 前例でセットを使いましたが 他のオプションもあります
これらのデータ構造を使用し選択を表現できます SwiftUIはmacOS Venturaの新機能の単一選択 macOSサイドバーや複数選択をサポートします
iPadOS 16では軽量な 複数選択も導入されています キーボードを取り付けると 編集モードに入る必要なく複数行を選択し モダリティを回避できるようになりました キーボードを使用する場合 shiftやcommandなどの ショートカットを使い 選択範囲の拡張や変更ができます これはポインターとの相性も抜群です 選択を採用した場所の テーブルはこんな感じです キーボードとトラックパッドが付属し 行のインデントがされていないが 選択されていることに変わりはないです タッチを使う場合は 編集モードに入る必要があり 2本の指のパン使うことで加速されます SwiftUIは自動的にこのジェスチャーを サポートします 編集モードといえば 単一選択と編集モードに関する いくつかのアップデートがあります iOS 16では iPhoneとiPadで リストを選択する時に 編集モードが不要になりました 更新されたNavigation APIとの連携に 大変便利です
これらの更新によりこの表は 編集モードの新しい列を追加することで 前の表の上に構築されることになります 編集モードはキーボードなしで 行う場合のみ必要です 場所のテーブルを更新して 選択をサポートします 選択範囲を保存する状態を追加することで 簡単に場所のテーブルに選択範囲を 追加することができます ステートを作成した後 テーブルの初期化子に バインディングを渡します テーブルは選択の型が その行の識別子との一致を強制します それで 場所ID型を選択型として使用します 複数選択したいので選択状態には セットを使っています テーブルの行は自動的にタグ付けされます
そして テーブルの行を 選択できるようになりました しかし 私は選択で本当に何もしていません 選択した場所をガイドに追加して 読書会で他の人と共有できるボタンが あればいいと思います これはツールバーボタンを 追加するためのコードです 空でない選択範囲があれば ボタンは今すぐ表示されます また 編集ボタンも追加しました 既存の軽量な選択機能を補完するものですが キーボードがないときに編集モードに入り 終了するためのアフォーダンスです 優れたAppはキーボードの 使用は関係なく勝っており 編集モードへの移行や終了の コントロールが重要です
もう一息です これで 行を選択したときに表示されるボタンと 編集モードに入り終了するボタンができました ツールバーの詳細については このシリーズの第2回目を 必ずご覧ください ツールバーボタンにかなり満足していますが もっとできることがあります 選択に関するアクションはできるだけ簡単に アクセスできるようにしておくとよいでしょう iOS 16 iPadOS 16 macOS Venturaで SwiftUIが複数選択の コンテキストメニューに対応しました 複数選択可能なコンテキストメニューにより 選択された識別子を扱う コンテキストメニューが表示されます 理解を深めるため テーブルの構造を 調べてみましょう
コンテキストメニュ―の バリエーションは3つです まず 上部の選択項目のように 複数の項目でメニュ―を 表示することができます
個別の項目に コンテキストメニューの表示もできます
最後に コンテンツのない空の領域に コンテキストメニューを 表示することができます
場所のテーブルに サポートを追加してみましょう
コンテキストメニューに焦点を当てるため これまでのコード例で いくつか詳細を省略しました 選択型の新しいcontextMenu モデファイアを追加しました これはリストやテーブルの 選択型に合わせる必要があり テーブルを使用しているので 場所ID型を使用します
クロージャは作用する項目の セットを渡されるので 空の場合は メニューは空の 領域に対するものです 空の場所に新しい場所を 追加できれば効果的です 外出先で新たに静かな読書ができる 場所を見つけたらすぐに追加できます アイテムの空のセットのためのView builderが Viewに解決されない場合 SwiftUIは空の領域のメニューを表示しません 次に単一の選択範囲を処理してみましょう セットが一つしかない場合 メニューは一箇所だけ表示されています 単一選択と複数選択でこれら場所のテーブルを ガイドに追加できるようにしたいので 別のViewをメニューに追加します 進捗状況を確認してみましょう 新しいコンテキストメニューのサポートです 空の部分に追加するメニュー項目が 表示されます 1つの行の選択で その行のコンテキストが表示されます キーボードで選択範囲を拡張して この青いハイライトを作ることができます 複数の行に対してコンテキストメニューを 表示させ簡単に場所を追加できます
テーブルがおしゃれになりましたので テーブルの周りに何か構造を 追加する時期だと思います そのためにはSplit Viewが必要ですね ナビゲーションはiPadの基本的な体験です ドリルインの必要がなく 一度に情報を表示できるSplit Viewは モダリティを回避する素晴らしい方法です ナビゲーションとSplit Viewに関する SwiftUIのいくつかのアップデートを カバーするつもりです 前のセクションでは 場所のテーブルを作成し 選択や編集モードなどの 豊富な機能を追加しました ただ構成が少し足りないと思います そこで NavigationSplitViewを活用して Appの構造の基礎を構築していきます iPadOS 16とmacOS Venturaの新機能として SwiftUIはNavigationSplitViewタイプによる Split Viewのサポートが改善されました SwiftUIは2列または3列の 分割表示をサポートし 列の表示方法を詳細に制御するための 複数のスタイルを持っています ナビゲーションコンテンツの 表示方法については このセッションで 完全には説明しませんので SwiftUI cookbookをチェックしてみてください Curtは本当に美味しい ナビゲーションを体験する レシピをたくさん持っています その代わり私は Split Viewに もっと集中します iPadでの2列Split Viewを行う場合の図です SwiftUIでは 先頭の列はサイドバーとよばれ 末尾の列は詳細の列と呼ばれます 列がバランスよく配置されていることに 注目です 横向きでだとSwiftUIは この表示をデフォルトで提供します 縦向きではサイドバーは隠れ 詳細の列だけが表示されます サイドバーボタンをタップすると 薄暗くなった詳細列の上に サイドバーが表示され
一般的に2列のSplit Viewは スペースに制約がある場合 詳細列のみを表示することが 望ましいとされています 詳細列にはサイドバー列よりも 重要な情報が表示されることが多いからです この動作をカスタマイズしたい場合は ProminentDetailNavigationSplitViewStyleで 常に詳細列を優先するか balancedNavigationSplitViewStyleで 重み付けのバランスをとることができます NavigationSplitViewは 3列レイアウトにも対応します 3列の場合 サイドバーと詳細の間に 内容欄と呼ばれる列が追加されます UIKitの場合これが補足欄です 横向きではコンテンツと詳細列が 表示され サイドバーの切り替えが可能です ツールバーボタンをタップすると 詳細列がスライドし サイドバーとコンテンツが示されます 縦向きでは 詳細列のみ表示され ツールバーボタンをタップすると コンテンツが表示されます もう一度タップすると サイドバーが表示されます サイドバーとコンテンツ 両方が詳細列に重なります
一般に3列のSplit Viewは 自動スタイルの使用をお勧めします 利用可能なスペースを最大限に活用し より大きなディスプレイに 特化しているからです 2列Split Viewと同様に 3列Split Viewも コンパクトサイズのクラスで スタックに折りたたむことができます Split Viewの基本を説明したところで Split Viewsを追加してみましょう こちらはコンテンツViewです 2列のNavigationSplitViewを作成しました 1列目がサイドバー列で 2列目が詳細列です 詳細列にはサイドバー列の リンクが表示されますが 何も表示されない場合は 「select a place」の プレースホルダーが表示されます
プレースホルダーのスクリーンショットです 自動スタイルを使用しており 横向きでサイドバーを表示し 縦向きでは邪魔にならないように隠します サイドバーの行のタップで その行が詳細列に示されます スライドオーバー使用で 列は自動的に折りこまれます これは氷山の一角です ナビゲーションの追加機能として 復元やディープリンクのサポートが強化され プログラムによる制御もより豊富になりました The SwiftUI cookbook for navigationで ご確認ください
このAppには素晴らしいiPadの 機能が組み込まれているので 心安らぐ読書場所を探しに行くのが楽しみです 早く読書会に追いつくといいのですが このセッションでテーブルを活用して データを豊富に表示する方法 高度な選択インタラクションを管理する方法 そしてSplit Viewsでモダリティを 回避する方法について説明しました
関連するセクションをチェックして iPadのパワーを活用した SwiftUI Appに磨きをかけてください
ありがとうございました
-
-
3:10 - Places List
struct PlacesList: View { @Binding var modelData: ModelData var body: some View { List(modelData.places) { place in PlaceCell(place) } } }
-
3:18 - Places Table
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) } } }
-
10:25 - Places Table with selection
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 } } }
-
10:26 - Places Table toolbar additions
Table(modelData.places, selection: $selection, sortOrder: $sortOrder) { ... } .toolbar { ToolbarItemGroup(placement: .navigationBarTrailing) { if !selection.isEmpty { AddToGuideButton(selection) } } ToolbarItemGroup(placement: .navigationBarLeading) { EditButton() } }
-
12:34 - Item context menus
// 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) } }
-
16:55 - Navigation Split View example
// Navigation Split View example struct ContentView: View { var body: some View { NavigationSplitView { SidebarView() } detail: { Text("Select a place") } } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。