ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
iPadのSwiftUI:ツールバーやタイトルなどを追加する
SwiftUIでiPad Appのツールバーを調整する用意はよろしいでしょうか。iPadのスペースを活用できるようツールバーを構造化する方法や、ユーザーが自身の生産性を最大限に高められるようにする方法を紹介します。また、カスタマイズ化や、ドキュメントの表示方法などに関する最新情報についても解説します。 これは、2部構成シリーズの後半セッションになります。この動画を最大限に活用するには、「iPadのSwiftUI:インターフェイスをオーガナイズする」からご覧ください。
リソース
関連ビデオ
WWDC23
WWDC22
-
ダウンロード
SwiftUIチームのエンジニア Harryです SwiftUI on iPadシリーズの 第2部へようこそ 第1部ではRajが リストと表や セレクションやSplit Viewで iPadの大きいスクリーンと インプットデバイスを使い Appを向上させる方法を お話ししました ご覧でないなら そちらから始めることを お勧めします Rajが素晴らしい「Places」Appを 作成しました そこに機能を足そうと 思います そこで このセッションでは 僕の大好きな ツールバーに焦点を当てます SwiftUIはToolbar APIで さまざまなシステムバーを設計でき iOSのナビゲーションバーや 下部のバー macOSのウインドウ ツールバーがその例です ツールバーはよく使用する 機能への近道を提供します いいツールバーはユーザーの 生産性を向上します
ツールバーのことを 考えていましたが PlacesでiOS 16の新しい ツールバー機能を使用できます iPadでどんなことが 可能になるか 僕の作ったものを 少しお見せしましょう
これがPlaces Appの 完成品です
新しい機能として左揃えの ナビゲーションタイトルや タイトルメニューに タイトルメニューヘッダ 中央揃えのツールバーがあります Macを使うなら 見慣れた機能である ツールバー カスタマイゼーションは ツールバーを自分のものに 変更することができます これらのMacの強力な機能が iPadにも登場します
まず最初に Toolbar APIの 向上点をご紹介します そしてタイトルとドキュメントの 新しいAPIをご紹介します では まずはツールバーです すでにiOSでツールバーを 設定された方も多いでしょう 小さいスクリーンを 有効に使うため このようにメニューを 追加したかもしれません
コードではこうなります
primaryActionのある ToolbarItemがあります その中にコントロールを コンテンツにした さらなるメニューがあります ではiPadで見てみましょう ご想像通り あまりスペースを 有効に使っていません しかし 嬉しいことに iOS 16では ツールバーはこのような メニューを具体化します オーバーフロー メニューと呼び 有効に使うため ツールバーのコンテンツを 作り直しましょう
まずToolbarItemを ToolbarItemGroupに変換し メニューを取り除き そのコンテンツを ToolbarItemGroupの コンテンツにします これにより各ツールバーアイテムを グループのビューに挿入します iPadとMacでは これだけで 自動的にオーバーフロー メニューに動かせるのです まだできることが ありますが その前に ツールバーアイテムの プレースメントを考えます プレースメントはツールバー アイテムが表示されるエリアです 違うプレースメントを 同じエリアに配置できます 3つのエリアがある ナビゲーションバーのように 左と中央と右エリアがあります 左と右に通常 コントロールがあります 中央にはAppの ナビゲーションタイトルがあります ではPlacesで見てみましょう
PlacesではprimaryAction ToolbarItemGroupが iPadとMacの 右エリアにあります primaryActionsは そのスクリーン上での 最も共通的アクションを 示します iOS 16ではsecondaryAction という新しいplacementがあります これらは頻繁に使われる コントロールではないものの 独自のツールバーアイテムを 持つことができます お気に入り追加や編集は Placesでは重要ではないので secondaryActionとします
secondaryActionはツールバーでは 見えないのがデフォルトです その代わりにオーバーフロー メニューにあります その行動は新しいtoolbarRole 修飾子で変更します
この修飾子は意味役割を 割り当てることで影響を与え ここではエディタとして ナビゲーションバーに コンテンツ編集用に 最適化するべきだと伝えます ナビゲーションバーは ツールバーアイテムの表示に スペースが要ると解釈し ナビゲーションタイトルを 中央から左エリアに 移動させます これでオーバーフロー メニューに動かす前に secondaryActionを 中央に配置できます コンパクトサイズクラスでは ナビゲーションバーは変わらず 中央でタイトルを 表示し続けます
secondary actionと toolbar role APIで PlacesはiPadのサイズを 有効に使えるようになります 中央が使えるようになり ツールバーにアイテムを足せます ノイズボタンや 快適レベルボタンや ゴミ箱ボタンなどです しかし足しすぎると ユーザーによっては 扱いにくくなります MacOSではツールバーの カスタマイズをサポートし ユーザーが使用しやすい ツールバーを選べます iPadOSもカスタマイズが できるようになりました macOSの既存のtoolbar customization APIで この機能を適応できます 見てみましょう カスタマイズできるのは ツールバーアイテムだけですので ToolbarItemGroupを ToolbarItemsに分けます 機能的な変化はありません またツールバーの アイテムそれぞれに ユニークな識別子が 必要になりますので それぞれIDを追加します IDはAppを通して一意で 一貫せねばなりません ユーザーがツールバーを カスタマイズする時 SwiftUIは これらのIDを要求し 表示するビューの参照に 使用するからです 最後にツールバー識別子にも IDを足します これでツールバーが カスタマイズできます
カスタマイズの特徴として ツールバーアイテムは最初 表示させないようにすることもできます これらはカスタム ポップオーバーとして初めて現れ 後にツールバーに追加できます 最初に現れないため 特定のワークフローに便利な アクションに向いています 見てみましょう
新しいアイテムを目立たせるため 現在のツールバーのアイテムを隠します
そして ShareLinkのある ツールバーアイテムを足します ShareLinkはTransferable という新しいプロトコルです これらの詳細については 「Transferableの紹介」 セッションご覧ください 設定が済み showsByDefaultを falseとすれば 最初にツールバーに現れません
ご覧のように共有リンクが カスタマイズポップオーバーに あります そこからツールバーに ドラッグできます いい機能ですよね 共有リンクも設定し ツールバーアイテムの 関係について考えます 共有リンクを ツールバーに移動した後 画像とマップアイテムが 離れてしまいました でもこれらは編集 コントロールですので その関係をツールバーで 確立しようと思います
iOS 16とmacOS Venturaは ControlGroupを使って この関係をサポートします 見てみましょう 画像とマップの 調整アクションが ツールバーに それぞれあります これらをグループ化するため 同じアイテムに動かします そしてControlGroupとして 囲みます
これで同じユニットとして ツールバーから動かせます なかなかの機能ですが ControlGroupの新APIで さらに高度なことも可能です ControlGroupに ラベルを提供することで このグループのアイテムは オーバーフローメニューに動く前に 独自の小さいメニューに 畳むことができます ツールバーが 出来上がってきました 最後にもう1つ変更を加えます 新しい場所を足すのは よくある重要なアクションなので そのツールバーアイテムを 足してみます
そのためにツールバーに 新しいボタンを足します でもこれは最もよくある アクションだと思うので 今回は primaryActionを使用します
これはiOSとmacOSの 重要で明確な違いです macOSでは修飾子内全部が カスタマイズできますが iPadOSでは secondaryActionだけです ですので右のエリアで レンダリングする新しいボタンは ユーザーによって カスタマイズできません わあ! 情報満杯ですね でもこれだけではありません ナビゲーションタイトルにも 新機能があります メニューやドキュメントもそうです ドキュメントを見てみましょう いろんな種類があります Document group APIに 親しみがあるかもしれません ドキュメントグループには ドキュメントの管理に役立つ たくさんの ビルトイン機能があります ドキュメントグループを使うと これらはすべて無料で行うことができます
しかしPlacesではドキュメント グループを使用していなくても それぞれの場所が ドキュメントと考えられます 編集できるプロパティがあり Places appで 追加・削除ができ 友達とも共有できます ドキュメントグループベースでない Appで これをどう見せられるか 見てみましょう すでにplace nameを ナビゲーションタイトルに関連させ ツールバーのタイトルを この場所に関連づけています iOS 16では新しいナビゲーション タイトルの修飾子で さらに先へ 行くことができます ナビゲーションタイトルは メニュー表示をサポートできます macOSの「ファイル」メニューのようなものと 考えてください これを作るには ナビゲーションタイトルに アクションを提供します 普通のメニューと同様です タイトルに メニューインジケータが付き メニューにアクションが あることを示唆します それだけではありません 僕のお気に入りは編集可能 タイトルのサポートです ナビゲーションタイトルに バインディングをパスし ツールバーにタイトル編集の サポートを伝えます 唯一ないのは編集を 始める方法だけです タイトルメニューのアクション内で RenameButtonを 使用することができます
リネームボタンをタップし タイトルを改名します さらに ビューにナビゲーション タイトルを関連づけるように ドキュメントを 関連づけることができます ドキュメントが提供されると プレビュー付きの特別な ヘッダーが表示されます プレビューはドラッグ可能で 共有クイックアクセスがあります これらはナビゲーション ドキュメント修飾子で ナビゲーションドキュメントを ビューに関連付けすることで実現できます transferableに従うタイプか URLを直接 提供することで可能です ここでは今見ている場所を マップで開くURLを提供します URLを提供すればナビゲーション ドキュメント修飾子は macOSのウインドウツールバーの プロキシアイコンも設定します
これでツールバーの作成から 一息取ることができます 多くの機能を 追加できたでしょ? 早く使いたいですね iPadでのPlaces Appの向上を たくさんカバーしました iPadのツールバーには 多くの新機能があります オーバーフローメニューや カスタマイぜーションがその例です secondaryAction配置 とcustomization APIで iPadとMacの大きい スペースを有効に使えます
タイトルもまた新しい機能が 追加されました ナビゲーションタイトルや タイトルメニューの作成などです ナビゲーションドキュメント 修飾子を使いましょう SwiftUI on iPadシリーズを お楽しみいただけましたか? 表や選択できるツールバーなどで iPad Appのレベルを さらに上げましょう ありがとうございました
-
-
0:01 - Explicit More Menu
PlaceDetailContent(place: $place) .toolbar { ToolbarItem(placement: .primaryAction) { Menu { FavoriteToggle(place: $place) AdjustImageButton(place: $place) AdjustMapButton(place: $place) } label: { Label( "More", systemImage: "ellipsis.circle") } } }
-
0:02 - Menu in ToolbarItemGroup
PlaceDetailContent(place: $place) .toolbar { ToolbarItemGroup(placement: .primaryAction) { Menu { FavoriteToggle(place: $place) AdjustImageButton(place: $place) AdjustMapButton(place: $place) } label: { Label("More", systemImage: "ellipsis.circle") } } }
-
0:03 - ToolbarItemGroup with Menu Content
PlaceDetailContent(place: $place) .toolbar { ToolbarItemGroup(placement: .primaryAction) { FavoriteToggle(place: $place) AdjustImageButton(place: $place) AdjustMapButton(place: $place) } }
-
0:04 - Secondary Action ToolbarItemGroup
PlaceDetailContent(place: $place) .toolbar { ToolbarItemGroup(placement: .secondaryAction) { FavoriteToggle(place: $place) AdjustImageButton(place: $place) AdjustMapButton(place: $place) } }
-
0:05 - Toolbar Role
PlaceDetailContent(place: $place) .toolbar { ToolbarItemGroup(placement: .secondaryAction) { FavoriteToggle(place: $place) AdjustImageButton(place: $place) AdjustMapButton(place: $place) } } .toolbarRole(.editor)
-
0:06 - Individual ToolbarItems
PlaceDetailContent(place: $place) .toolbar { ToolbarItem(placement: .secondaryAction) { FavoriteToggle(place: $place) } ToolbarItem(placement: .secondaryAction) { AdjustImageButton(place: $place) } ToolbarItem(placement: .secondaryAction) { AdjustMapButton(place: $place) } } .toolbarRole(.editor)
-
0:07 - Customizable ToolbarItems
PlaceDetailContent(place: $place) .toolbar(id: "place") { ToolbarItem(id: "favorite", placement: .secondaryAction) { FavoriteToggle(place: $place) } ToolbarItem(id: "image", placement: .secondaryAction) { AdjustImageButton(place: $place) } ToolbarItem(id: "map", placement: .secondaryAction) { AdjustMapButton(place: $place) } } .toolbarRole(.editor)
-
0:08 - ShareLink ToolbarItem
PlaceDetailContent(place: $place) .toolbar(id: "place") { ToolbarItem(id: "favorite", placement: .secondaryAction) { FavoriteToggle(place: $place) } ToolbarItem(id: "image", placement: .secondaryAction) { AdjustImageButton(place: $place) } ToolbarItem(id: "map", placement: .secondaryAction) { AdjustMapButton(place: $place) } ToolbarItem(id: "share", placement: .secondaryAction) { ShareLink(item: place) } } .toolbarRole(.editor)
-
0:09 - Non-default ShareLink ToolbarItem
PlaceDetailContent(place: $place) .toolbar(id: "place") { ToolbarItem(id: "favorite", placement: .secondaryAction) { FavoriteToggle(place: $place) } ToolbarItem(id: "image", placement: .secondaryAction) { AdjustImageButton(place: $place) } ToolbarItem(id: "map", placement: .secondaryAction) { AdjustMapButton(place: $place) } ToolbarItem(id: "share", placement: .secondaryAction, showsByDefault: false) { ShareLink(item: place) } } .toolbarRole(.editor)
-
0:10 - ControlGroup in ToolbarItem
PlaceDetailContent(place: $place) .toolbar(id: "place") { ToolbarItem(id: "favorite", placement: .secondaryAction) { FavoriteToggle(place: $place) } ToolbarItem(id: "image", placement: .secondaryAction) { ControlGroup { AdjustImageButton(place: $place) AdjustMapButton(place: $place) } } ToolbarItem(id: "share", placement: .secondaryAction, showsByDefault: false) { ShareLink(item: place) } } .toolbarRole(.editor)
-
0:11 - ControlGroup in ToolbarItem with Label
PlaceDetailContent(place: $place) .toolbar(id: "place") { ToolbarItem(id: "favorite", placement: .secondaryAction) { FavoriteToggle(place: $place) } ToolbarItem(id: "image", placement: .secondaryAction) { ControlGroup { AdjustImageButton(place: $place) AdjustMapButton(place: $place) } label: { Label("Edits", systemImage: "wand.and.stars") } } } .toolbarRole(.editor)
-
0:12 - NewButton ToolbarItem
PlaceDetailContent(place: $place) .toolbar(id: "place") { ToolbarItem(id: "new", placement: .primaryAction) { NewButton() } ToolbarItem(id: "favorite", placement: .secondaryAction) { FavoriteToggle(place: $place) } ToolbarItem(id: "image", placement: .secondaryAction) { ControlGroup { AdjustImageButton(place: $place) AdjustMapButton(place: $place) } label: { Label("Edits", systemImage: "wand.and.stars") } } ToolbarItem(id: "share", placement: .secondaryAction, showsByDefault: false) { ShareLink(item: place) } } .toolbarRole(.editor)
-
0:13 - Navigation Title
PlaceDetailContent(place: $place) // toolbar customizations ... .navigationTitle(place.name)
-
0:14 - Navigation Title with Menu
PlaceDetailContent(place: $place) // toolbar customizations ... .navigationTitle(place.name) { MyPrintButton() }
-
0:15 - Editable Navigation Title with Menu
PlaceDetailContent(place: $place) // toolbar customizations ... .navigationTitle($place.name) { MyPrintButton() }
-
0:16 - Editable Navigation Title with RenameButton
PlaceDetailContent(place: $place) // toolbar customizations ... .navigationTitle($place.name) { MyPrintButton() RenameButton() }
-
0:17 - Navigation Document
PlaceDetailContent(place: $place) // toolbar customizations ... .navigationTitle($place.name) { MyPrintButton() RenameButton() } .navigationDocument(place.url)
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。