ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
デスクトップクラスのiPad Appを構築する
デスクトップクラスの機能を活用するiPad Appの作成方法をご覧ください。UIKitチームのMohammedより、最新のナビゲーション、コレクションビュー、メニュー、編集APIを解説します。強力な iPad Appを構築するベストプラクティスも紹介しますので、是非ご覧ください。このセッションに合わせて同時にコーディングしたり、サンプルAppをダウンロードして、自分のコードを更新する際の参照用として使用してください。
リソース
- Building a desktop-class iPad app
- centerItemGroups
- collectionView(_:contextMenuConfigurationForItemsAt:point:)
- collectionView(_:performPrimaryActionForItemAt:)
- Supporting desktop-class features in your iPad app
- titleMenuProvider
- UIDocumentProperties
- UINavigationItem.ItemStyle
- UINavigationItemRenameDelegate
関連ビデオ
WWDC23
WWDC22
-
ダウンロード
♪ ♪
こんにちは UIKit チームの Mohammed です デスクトップクラスの iPad App を構築するための 詳細に関するセッションに ご参加ありがとうございます ここでは iPadOS 16 の API を 使って 既存の iPad App を デスクトップクラスの体験に アップデートします まずは新しいナビゲーションバー API で 強力な機能を表示し UIの密度を高め カスタマイズ性の提供を目指します
そして 新しい UICollectionView と メニュー API を採用し 複雑なワークフローと 複数選択時のクイックアクションを実現します 新しい検索と置換を有効にし 新しい編集メニューで テキスト編集を強化します アップデートするのは iPadOS 15 向けの Markdown エディタです モダナイゼーションプロセスの 各ステップを通じて ベストプラクティスとその選択の 背景となる動機について説明します
初めての方は UIKit の新しい iPadOS の API を網羅した 「デスクトップクラスのiPadの紹介」や 最高のデスクトップクラスの iPad App を デザインするヒントを紹介している 「iPad App設計の最新情報」 を先にご覧ください では 早速始めましょう まず始めに App の操作系を考えてみましょう この App は iPadOS 15 向けに 設計されているため すでに最も重要なコントロールが ナビゲーションバーに表示され 副次的なコントロールはさまざまな メニューやポップオーバーに配置されています
iPadOS 16では UIKit は既存のナビゲーション スタイルを正式なものとし より密でカスタマイズ可能なレイアウトを持つ 2つの新しいものを導入しています これにより App は より多くの機能を UI の前面に押し出しながら コンテンツに最も適したレイアウトを 表現することができます
ナビゲーター App はおなじみの Push/Popナビゲーションモデルで 一般的に「設定 App」のような 階層的なデータを表示するのに適しています
Safari や ファイル App のようなブラウザは 複数のドキュメントや フォルダ構造に目を通したり その間を行き来したりするのに理想的です
エディタは個々のドキュメントを集中的に表示 または編集するのに適しています
Markdownエディタである私たちの App には このスタイルは最適な選択です
エディタースタイルでは タイトルをバーの先端に配置し 中央を新しいアイテム用に開放しています これにより他のビューやメニューで 隠されていた機能を 表に出す事ができるようになります デザインを生かすため いくつか工夫をしています まずビルトインされている 「戻る」をカスタマイズします 次にタイトルメニューにドキュメント情報と 一般的なドキュメントアクションを追加します 新しいリネーム UI のサポートも追加します 最後に これまで埋もれていた機能を バーの中心に持ってきて アクセスを容易にします まず navigationItem の style プロパティを .editorにして エディタスタイルを選択します
すぐにタイトルが先頭揃えとなり 中央の領域が開放されます
末尾の done ボタンを削除して 新しい backAction APIで 置き換えます このビューを閉じてドキュメントピッカーに 戻るアクションが標準的なものになります
App にタイトルメニューは必要かどうかを 考えてみましょう タイトルメニューはナビゲーションバーの タイトルビューから表示されます ドキュメントのメタデータや ドキュメント全体に適用するアクションの 表示に適しています App がドキュメントベースでない場合は ビュー全体に適用されるアクションを 表示すると良いでしょう ドキュメントメニューのヘッダーを使用して ドキュメントに関する情報を表示するのは 理にかなっています またドキュメントはドラッグ可能なので 共有機能を簡単に利用できます いよいよコードを書く時がきました
App は UIDocument に対応しているので UIDocument の fileURL で UIDocumentProperties オブジェクトが インスタンス化できます
次に 同じ URL を使って NSItemProvider を作成します
itemProvider で UIDragItem を作成し properties の dragItemsProvider に設定
UIActivityViewController も作って properties の activityViewControllerProvider に設定 properties オブジェクトを エディタビューコントローラの navigationItem の documentProperties に設定します このヘッダーにはドキュメントの名前やサイズ アイコンが表示され ドキュメントの概要が一目でわかります ドラッグアイテムとアクティビティビュー コントローラーのプロバイダを指定したので アイコンをドラッグして ドキュメントを App の外にコピーしたり シェアボタンをタップしてアクティビティ ビューコントローラーを表示できます
タイトルメニューは ドキュメントヘッダーの表示だけでなく ドキュメント全体に適用する 機能の提供に適しています メニューに表示できるアクションは2種類で ローカライズされたタイトルやシンボル画像を あらかじめ定義したシステムが提供するものと App 提供の完全なカスタムなものです
いくつかの追加アクションが付属しているので renameアクションから見てみましょう rename delegateプロトコルに準拠することで このアクションをメニューに追加できます トリガーすると バー内蔵の リネーム UI を表示します
ビューコントローラを ナビゲーションアイテムの
renameDelegate とし 表示中の名前を変更するため navigationItemDidEndRenamingWithTitle を実装します
この関数は リネームアクションが コミットされると呼び出され 実際のリネームとその処理は App 側の責任となります API は意図的にオープンエンドになっており App のあらゆるデータモデルをサポートします システムが提供する 他のアクションを見てみましょう エディタビューコントローラで それらの関数をオーバーライドします ここでは duplicate と move の関数を実装しました UIKitは navigationItem の titleMenuProvider に rename アクションを含む システム提供のアクションを 提案された UIMenuElements の配列として 自動的に表示します タイトルメニューに含めるには 返されたメニューの子メニューに追加します
システムが用意したアクションに加えて 完全にカスタム化されたアクションや メニュー階層全体の追加も可能です ここでは HTMLとPDFとしてエクスポートする サブアクションを持つエクスポートの サブメニューを追加しました
タイトル表示をタップすると ドキュメントのヘッダーと 追加したすべてのアクションを含む メニューが表示されます 名称変更を選択すると 組み込みの名前変更 UI が起動し ドキュメントの名前が変更できます
App の基本構造ができてきたので Mac catalyst で Appをビルドしたときの 見え方を確認しておきましょう Mac で実行するとタイトルを先頭に寄せた エディタースタイルが きれいに翻訳されています
「戻る」も引き継がれクリックすると ファイルブラウザが表示されます
システムが提供するアクションとリネーム機能は App のファイルメニューに自動的に表示されます Mac Catalyst では titleMenuProvider は呼ばれないので カスタムアクションは Fileメニューに含まれません アクションを公開するには UIMenuSystem を使って App のメインメニューに手動で追加します
モダナイゼーションを続けましょう 目標に向かっているMacに これからも注目しましょう バーのセンターエリアについて考えてみます iOS 15版では多くの副操作や ツール保持のメニューが用意されており センターアイテムが発見し易くしています
中央エリアはカスタマイズ可能で 使用頻度の低いコントロールで埋まる 心配はありません ワークフローに合わせて バーの内容を調整できます カスタマイズを有効にする 最初のステップとして ナビゲーションアイテムに customizationIdentifier を指定し
センターアイテムを UIBarButtonItemGroups で定義します グループは前からありますが iOS 16 では UINavigationBar に導入し カスタマイズのサポートを強化しています これは デフォルトで表示したい センターアイテムのセットです 左側のスクロール同期ボタンは この App で必須の機能を提供していますので UIBarButtonItem の新しい関数である creatingFixedGroup() を使って 固定グループへ配置しましょう 固定グループは 移動やカスタマイズはできません
リンクの追加ボタンは エディタでリンクタグを入力すると 同じ機能を実現できるので OptionalGroup を使用して カスタマイズ可能なアイテムとして作成し Appを再起動しても変わらないように 固有のcustomization Identifier を与えます
同様の手順でデフォルトセットの 残りの項目を定義し デフォルトで使用する必要のない 優先度の低い項目に移動します そのひとつが 太字 斜体 下線などの項目を含む テキストフォーマットグループです
カスタマイズのポップオーバーにのみ表示して バーにドラッグできるようにします
UIBarButtonItemGroup の optionalGroup イニシャライザで isInDefaultCustomization を false に設定します
またグループに代表的なアイテムを設定して ポップオーバーでタイトルを表示し バーのスペースが足りない時に 折りたためるようにします
iPad に戻ると定義した中央の項目が バーの中央に表示されます 追加した More ボタンをクリックすると ツールバーのカスタマイズアクションが メニューと一緒に表示されます クリックするとカスタマイズモードになります
固定とした「同期スクロールボタン」は 止まっていますが 他の項目は揺れてカスタマイズ可能になります
書式グループなどのオプション項目は ポップオーバーに表示され バーにドラッグできます
Mac で実行すると 中央の項目がカスタマイズ可能な macOSのツールバーボタンに変換されています
iPad に戻り Appのサイズを変更してみましょう ツールバーの空きスペースが少なくなったので 中央の項目は表示されなくなりました UIKit は利用可能なスペースに応じて センターアイテムの表示と非表示を 自動的に処理します サイズが合わない項目は オーバーフローメニューに表示されます 標準のバーボタン項目は 自動的にメニュー表現に変換されますが 好みに応じてカスタムメニューを提供できます UIKit はカスタムビューアイテムの目的を 理解できないので スライダーは自動的に変換されません メニューをカスタマイズする必要があります
私たちのスライダーのアイテムを紹介します カスタムビューを持つ単一のバーボタン項目で オプションのバーボタングループに含まれます スライダーのコア機能を提供するために 減少 リセット 増加のアクションを持つ UIMenu として定義することにします
UIMenu の新しい preferredElementSize プロパティを使用すると コンパクトなサイド バイ サイドの 外観にすることができ
新しい keepMenuPresented プロパティを使用すると 各アクションの実行後も メニューの表示を閉じずに フォントサイズを何回でも変更できます もう一度 iPad で実行してみましょう オーバーフローメニューを表示すると スライダーがインラインメニューとして 表示され 3つのアクションが横並となり スライダーの全機能をカバーします
Mac ではスモールエレメントサイズが 存在しないため アクションは macOS の標準的な メニュー項目として表示されます UI の構成とカスタマイズは以上です 次に 新しいコレクションビューと メニュー API で App 内のワークフローの高速化を検討します この App のサイドバーは目次になっており ドキュメントの素早いナビゲートや トップレベルのタグにアクションを起こせます iOS 16 以前は 複数の項目の編集機能を追加する場合 個別の編集モードを実装し 一括操作は ツールバーのボタンに 委ねていました
iOS 16 では複数アイテムのメニューに 新しいデザインが導入され メニューがどのアイテムに 影響するのか明確になり 複数アイテムのドラッグが行えるように アイテムの集合が表示されるようになりました デスクトップクラスの iPad App では 新しいメニューデザインは 軽量な選択スタイルとの組み合わせが最適です 「軽量」とは コレクションビューを 編集モードにしたり App の UI に大きな変更を加えずに 複数のアイテムが選択できることです まず既存の API で キーボードフォーカスを有効にします allowsMultipleSelection を true に設定してから
allowFocus を true にして有効にします
selectionFollowsFocus を true にして
選択範囲にフォーカスが移動するようにします iPad でこれを実行すると 各アイテムが選択範囲に追加されても 選択アクションが実行され エディタビューが スクロールすることに気づきます コードに戻り何が起きたか見てみましょう
この通りです didSelectItemAtIndexPath のコードは collectionViewの isEditingプロパティを見て 編集モード中のスクロールを無効にしています 編集モード以外でも 複数選択が可能になったので このコードは選択されるたび実行されます これは UICollectionViewDelegate メソッドで 解決できます performPrimaryActionForItemAtIndexPath を 実装して スクロールのコードを この新しい関数に移動します この関数は 単一のアイテムが タップされたときにだけ呼び出され コレクションビューは編集中ではないので 編集モードの確認は不要です
選択に関する動作もないので indexPath での select item の実装も削除できます
iPad に戻り 複数の項目を選択しても エディタビューで対応するテキストに スクロールしなくなりました 実際にメニューのサポートを追加してみます
iPadOS 16 では UICollectionViewDelegate の 既存の単一アイテムメニューメソッドは 非推奨です 代わりに 0から多数の メニュー表示に対応します indexPaths 配列に含まれる項目の数は 選択された項目の数と メニューが呼び出された場所に依存します
配列が空の場合はセル間の空白部分に メニューが呼び出されており
indexPathが1つであれば 非選択項目か唯一の選択項目に対して 起動されたことになります
複数の項目がある場合は 複数選択されている項目に対して メニューを起動しています
iPad に戻り再び上位4項目を選択し 選択した項目のひとつを 2本指でクリックすると 新しい複数項目メニューが表示されます
Mac で同じことを行うと 選択したセルの周りにリングが描かれ ハイライトされます
複数項目のメニューができたので 新しい検索と置換 編集メニューの機能を使って テキスト編集の体験を向上させましょう このApp はエディタに UITextView を使用しており カスタム検索と置換のアクションは不要なので デフォルト機能を有効にするために テキストビューの isFindInteractionEnabled プロパティを true にします この状態で テキスト編集中に Command+F を押すと 検索と置換のUIが表示されます
テキストビューの編集メニューに カスタムアクションを追加することで 簡単に素晴らしい編集機能が実現できます UITextViewDelegate メソッドの editMenuForTextIn: suggestedActions: の実装だけです 実装では このHideアクションのような カスタムアクションと システムメニューと組み合わせた UIMenu を構築して返すことができます
これがその結果です テキストを選択して編集メニューを出すと 弊社独自のアクションと システム提供のアクションの両方が表示されます 検索と置換や編集メニューの詳細は 「デスクトップクラスの編集操作を 取り入れる」のセッションを ご覧ください これで全てです! これらの変更で App を デスクトップクラスにして Mac にシームレスに翻訳するための 素晴らしい基本的なステップを踏み出しました iPadOS 16で提供されるAPIで 自分のAppを同様のプロセスで動かせます App に合うナビゲーション スタイルを選択したら プロパティとタイトルメニューで ワークフローを強化します センターアイテムに重要な機能を配置し カスタマイズを提供します マルチアイテムメニューで 複数のアイテムに対応します 検索と置換や新しい編集メニューを使って App のテキスト編集の体験を向上させます 新しい App の作成にしても 既存の App のアップデートにしても 新しいツールを使った あなたの App を使うのが待ちきれません ご視聴ありがとうございました
-
-
3:36 - Enable UINavigationBar editor style.
navigationItem.style = .editor
-
3:52 - Set a back action.
navigationItem.backAction = UIAction(…)
-
4:48 - Create a document properties header.
let properties = UIDocumentProperties(url: document.fileURL) if let itemProvider = NSItemProvider(contentsOf: document.fileURL) { properties.dragItemsProvider = { _ in [UIDragItem(itemProvider: itemProvider)] } properties.activityViewControllerProvider = { UIActivityViewController(activityItems: [itemProvider], applicationActivities: nil) } } navigationItem.documentProperties = properties
-
6:36 - Adopt rename title menu action and rename UI
override func viewDidLoad() { navigationItem.renameDelegate = self } func navigationItem(_ navigationItem: UINavigationItem, didEndRenamingWith title: String) { // Rename document using methods appropriate to the app’s data model }
-
7:09 - Adopt system provided title menu actions.
override func duplicate(_ sender: Any?) { // Duplicate document } override func move(_ sender: Any?) { // Move document } func didOpenDocument() { ... navigationItem.titleMenuProvider = { [unowned self] suggested in var children = suggested ... return UIMenu(children: children) } }
-
7:10 - Add custom title menu actions
func didOpenDocument() { ... navigationItem.titleMenuProvider = { [unowned self] suggested in var children = suggested children += [ UIMenu(title: "Export…", image: UIImage(systemName: "arrow.up.forward.square"), children: [ UIAction(title: "HTML", image: UIImage(systemName: "safari")) { ... }, UIAction(title: "PDF", image: UIImage(systemName: "doc")) { ... } ]) ] return UIMenu(children: children) } }
-
9:35 - Enable customization for center items
navigationItem.customizationIdentifier = "editorView"
-
10:00 - Define a fixed center item group.
UIBarButtonItem(title: "Sync Scrolling", ...).creatingFixedGroup()
-
10:23 - Define an optional (customizable) center item group.
UIBarButtonItem(title: "Add Link", ...).creatingOptionalGroup(customizationIdentifier: "addLink")
-
10:56 - Define a non-default optional center item group.
UIBarButtonItemGroup.optionalGroup(customizationIdentifier: "textFormat", isInDefaultCustomization: false, representativeItem: UIBarButtonItem(title: "Format", ...) items: [ UIBarButtonItem(title: "Bold", ...), UIBarButtonItem(title: "Italics", ...), UIBarButtonItem(title: "Underline", ...), ])
-
13:16 - Define a custom menu representation for a bar button item group.
sliderGroup.menuRepresentation = UIMenu(title: "Text Size", preferredElementSize: .small, children: [ UIAction(title: "Decrease", image: UIImage(systemName: "minus.magnifyingglass"), attributes: .keepsMenuPresented) { ... }, UIAction(title: "Reset", image: UIImage(systemName: "1.magnifyingglass"), attributes: .keepsMenuPresented) { ... }, UIAction(title: "Increase", image: UIImage(systemName: "plus.magnifyingglass"), attributes: .keepsMenuPresented) { ... }, ])
-
15:10 - Enable multiple selection and keyboard focus in a UICollectionView.
// Enable multiple selection collectionView.allowsMultipleSelection = true // Enable keyboard focus collectionView.allowsFocus = true // Allow keyboard focus to drive selection collectionView.selectionFollowsFocus = true
-
16:11 - Add a primary action to UICollectionView items.
func collectionView(_ collectionView: UICollectionView, performPrimaryActionForItemAt indexPath: IndexPath) { // Scroll to the tapped element if let element = dataSource.itemIdentifier(for: indexPath) { delegate?.outline(self, didChoose: element) } }
-
16:56 - Add a multi-item menu to UICollectionView.
func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemsAt indexPaths: [IndexPath], point: CGPoint) -> UIContextMenuConfiguration? { if indexPaths.count == 0 { // Construct an empty space menu } else if indexPaths.count == 1 { // Construct a single item menu } else { // Construct a multi-item menu } }
-
18:12 - Enable Find and Replace in UITextView.
textView.isFindInteractionEnabled = true
-
18:34 - Add custom actions to UITextView's edit menu.
func textView(_ textView: UITextView, editMenuForTextIn range: NSRange, suggestedActions: [UIMenuElement]) -> UIMenu? { if textView.selectedRange.length > 0 { let customActions = [ UIAction(title: "Hide", ... ) { ... } ] return UIMenu(children: customActions + suggestedActions) } return nil }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。