ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
より優れたドキュメントベースのアプリを構築
iPadOSの最新機能を使用して、ドキュメントベースのアプリを向上する方法を学びましょう。UIDocumentと既存のデスクトップクラスのiPadやドキュメントベースのAPIを活用して、アプリに新機能を追加する方法をご紹介します。データモデルをUIDocumentに変換する方法、UIDocumentViewControllerを使用したドキュメントの提示補法、最新のAPIへのアプリの移行方法、ベストプラクティスについて説明します。
関連する章
- 2:10 - Creating a document
- 5:46 - Presenting a document
- 9:38 - Migrating your app
リソース
関連ビデオ
WWDC23
WWDC22
WWDC20
Tech Talks
-
ダウンロード
♪ ♪
皆さんこんにちは 私はMichael Ochsです この動画では より優れたドキュメントベースのアプリを 構築する方法を説明します ドキュメントベースのアプリは 特にiPadの生産性向上ツールで 重要な役割を果たします ドキュメントベースのアプリは 3種類あります Filesアプリのような ドキュメントの検索を可能にするもの Quick Lookのような コンテンツの閲覧を可能にするもの そしてPages、Keynote Numbersなど コンテンツの編集や作成を 可能にするものです この動画ではビューアーと編集の アプリに焦点を置きます しかしここで話す内容は 閲覧アプリにも適用されます iPadOS 17には 多数の機能をアプリで自動的に有効にする 新しいビューコントローラーが 導入されています iPadOS 16で導入された デスクトップクラスのiPad APIと 既存のドキュメントベースのAPIと うまく連携します この新しいビューコントローラーは モジュラー方式で構築されています システムデフォルトも優れていますが 個々の動作をカスタマイズすることもできます
デスクトップクラスの iPad APIについては WWDC22 「Meet desktop-class iPad」と 「Build a desktop-class iPad app」を ご覧ください
SwiftUI開発に関しては DocumentGroupが追加のコードなしで これらすべての機能に対応するように なりました このSwiftUIに関しては WWDC20「Build document-based apps in SwiftUI」と WWDC22「SwiftUI on iPad: Add toolbars, titles, and more」をご覧ください UIKitでは機能がオプトインされています UIDocumentViewControllerはコンテンツの ビューコントローラーの新規ベースクラスで UIDocumentと連携し ナビゲーションバーを自動的に構成します これは 共有やドキュメントのDrag サポートの取り消しや再実行などの 機能を有効にします また自動リネームもサポートします この動画ではUIDocumentの使用方法と UIDocumentViewControllerで ドキュメントを提示する方法を説明します その後 どの機能が内蔵されているか それらをカスタマイズする方法について 説明します 最後は既存のアプリを移行して UIDocumentを活用するための ベストプラクティスの一部をご紹介します まずはドキュメントの作成です
すべてのドキュメントベースアプリの コアとなるのはUIDocumentです これはアプリがサポートする 各ファイルタイプの サブクラスとなるはずの 抽象基底クラスです すべてのUIDocumentsは URLにより裏付けされています ディスク上のファイルが 最も一般的ですが データベースと カスタムURLスキームを使用して ドキュメントを保存し 読み込むこともできます UIDocumentの読み込みと保存操作は 非同期型であるため 必要に応じて長い読み込みと 書き込みが可能になります そのため UIDocumentは スレッドセーフで ロックと待ち行列を通じて アクセスを調整します
UIDocumentサブクラスを実装する際に 2つの主要な責任を果たす必要があります ドキュメントの読み込みと保存と ドキュメントの内容への アクセスの提供です 読み込みと保存は すべてのドキュメントで類似しています 内容へのアクセスはドキュメントの種類や アプリで使用される方法によって異なります たとえば Markdownエディタの ドキュメントモデルには 単一のテキストプロパティのみ 存在する場合があります またはドキュメントの 個々の部分の更新を可能にする より複雑なインターフェースを 公開できる場合もあります 内容へのアクセスについて より詳細にお話しする前に 読み込みと保存について 説明しましょう 簡単なファイルベースのドキュメントは 2つの便利なメソッドで オーバーライドできます ドキュメントが開かれると loadFromContents:ofType:が ファイルの内容と共に呼び出されます ドキュメントが保存されると contentsForType:が呼び出され ドキュメントの現在の内容を取得します 通常のファイルのドキュメントの内容は データオブジェクトで それ以外はすべて FileWrapperとなります ファイルタイプとその機能の詳細については Tech Talksの 「Uniform Type Identifiers -- a reintroduction」をご覧ください この例では ドキュメントは 通常のマークダウンファイルに対処するので データオブジェクトを予期します 完全に制御したい場合は saveToURL:forSaveOperation:の代わりに readFromURL:とオーバーライドすれば URLへの完全アクセスを取得し 自由に読み込みと書き込みが できるようになります これはドキュメントをデータベースに 保存する場合や ドキュメントの読み込みと書き込みに 特別な要件がある場合に便利です 操作の保存は非同期ですが 読み込みはメソッドが返された時点で 完了するはずであることに注意してください ドキュメントの読み込みと保存については 以上です ではドキュメントの内容へのアクセスが 可能であることを確実にしましょう ドキュメントの内容へのアクセスを 提供するための簡単な方法は その内容にプロパティを作成する方法です この例では 完全なMarkdown記法が含まれている 単一のテキストプロパティを追加しました このプロパティは先程のスライドで 説明したように ドキュメントを初めて読み込む際に 設定されます アプリはユーザーが ドキュメントを編集するたびに テキストを更新します UIDocumentが保存が 必要であることを理解するために プロパティが更新されるたびに updateChangeCount:を呼び出します updateChangeCount:を呼び出すと UIDocumentはドキュメントを 保存する必要があることを記録し 適切なタイミングで自動的に保存します
次は新しいUIDocumentViewControllerで ドキュメントを提示する方法についてです
UIDocumentと同様に UIDocumentViewControllerも サブクラスであるべき 抽象基底のクラスです ドキュメントの開閉と保存を管理し 関連のドキュメントからの情報を使って ナビゲーションアイテムを入力します これにはタイトルナビゲーションアイテムの タイトルメニュー UIDocumentPropertiesオブジェクト 名前変更デリゲートが含まれます UIDocumentViewControllerは 取り消しや再実行などの 一般的なアクションの 主要なコマンドも提供します UIDocumentViewControllerの サブクラスの実装方法を見てみましょう
サブクラスでオーバーライドされるように 設計されているメソッドが2つあります ビューコントローラーに関連する ドキュメントが開かれた場合や すでに開いているドキュメントが ビューコントローラーに割当てられた場合 documentDidOpenが呼び出されます ビューコントローラーのビューに入力して このメソッドのドキュメントの内容を 表示します documentDidOpenが呼び出され ビューコントローラーのビューが 読み込まれるまでの時間は 保証されていないことに 注意してください 堅牢なコードを書くための良い方法は ビュー構成を 独自のメソッドに移動させ documentDidOpenとviewDidLoadの 両方から呼び出す方法です ビューを構成する前にビューが読み込まれ ドキュメントが開いていることを 確認してください
オーバライドする2つ目のメソッドは navigationItemDidUpdateです UIDocumentViewControllerが ナビゲーションアイテムに変更を加えると このメソッドが呼び出されます ここにナビゲーションアイテムの カスタマイゼーションを追加します UIDocumentViewControllerは 変更を最小限に抑えて 変更を最大限に維持するように 最善の努力を払います また UIDocumentViewControllerは undoRedoItemGroupを提供します 取り消しと再実行のボタンを表示する場合は このグループをナビゲーションバーに追加し ドキュメントにUndoマネージャーが 割当てられているかを確認してください UIDocumentViewControllerは Undoマネージャーの可用性に基づいて このグループの「非公開」 プロパティに変更を加え 必要に応じてグループ内で ボタンを有効または無効にします
UIDocumentViewControllerは 自動的にドキュメントを開閉します しかし ビューコントローラー以外から ドキュメントにアクセスする場合は openDocumentWithCompletionHandlerを 呼び出します UIDocumentViewControllerは documentDidOpenなど 必要なすべてのコールバックを行い 準備ができたら completionHandlerを呼び出します
最後になりますが UIDocumentViewControllerは ドキュメントプロパティを提供します このプロパティは常に ビューコントローラーに関連する ドキュメントを参照します 初期化中にドキュメントを提供できますが これはあくまでもオプションです ビューコントローラーに関連する ドキュメントがない場合は 自動的にエンプティステートを表示します エンプティステートの構成の 詳細については 「What's new in UIKit」をご覧ください また UIDocumentViewControllerは ルートビューコントローラーとしても使用できます 検索ビューコントローラーが 階層内にない場合 UIDocumentViewControllerは ドキュメントピッカーを開く ドキュメントボタンを ナビゲーションバーに追加します これにはアプリのinfo.plistの 関連するファイルタイプの UIDocumentClassキーを表し そのファイルタイプに一致するUIDocument サブクラスを設定する必要があります
iPadOS 17では UIDocumentは UINavigationItemRenameDelegateに従い ユーザーがタイトルメニューから 名前の変更を呼び出したときに 基礎的なファイル変更を独自で処理します UIDocumentViewControllerを 使用している場合 自動的に名前の変更が行われますが それ以外の場合は ドキュメントを ナビゲーションアイテムの名前変更 デリゲートとして手動で設定できます
これまでのすべての情報を活用すると iPadOS 17ですばらしい ドキュメントベースのアプリを 作成できます 次は既存のアプリを移行する方法です
新しいUIDocumentViewControllerを 使用するために簡単にアプリを移行できます これは3つの手順を踏むだけで完了します まずコンテンツビューコントローラーの ベースクラスをアップデートします 次に既存のコードを 新しいコールバックに移動します 最後に必要なくなったコードを削除します デスクトップクラスの iPadアプリの動画で使用する マークダウンエディタを 変換する方法を見てみましょう 詳しくなくても心配無用です まず既存のコードの 関連部分について説明します
上部にはビューコントローラーの定義 それを定義するドキュメントプロパティ 初期のドキュメントを設定し ドキュメントにコールバックを追加する initメソッドがあります
まずベースクラスを UIDocumentViewControllerに変更します
このクラスは UIDocumentViewControllerから継承するので コンパイラエラーが発生します なぜならプロパティドキュメントは 異なるタイプでスーパークラスに すでに存在するからです そのプロパティ名を 「MarkdownDocument」のように より具体的なものに変更します 次に ジェネリックドキュメントプロパティを 変換する計算プロパティを このビューコントローラーで使用される 特定のクラスにします 最後はInitializerに対処します ドキュメントにコールバックを 割り当てる必要がある唯一のコードです ドキュメントはこのビューコントローラーの 生存期間中に変更することができるので ドキュメントが変更されるたびに 実行されるよう移動します
これを行う簡単な方法は ドキュメントプロパティをオーバーライドし didSetコールバックを追加する方法です これでベースクラスが 最新のものになったので 新しいコールバックに対処します viewDidLoadで ナビゲーションバーにボタンを追加し ツールバーのカスタマイズが 可能になるように構成します UIDocumentViewControllerに関しては これをnavigationItemDidUpdateという 新しいコールバックに移動します
次に クラスにはすでに didOpenDocumentメソッドがあります これはUIDocumentViewControllerのものと ほぼ同じですが メソッドの名前を変更して ドキュメントがオプションに なったことに調整します
次は私たちが最も楽しむ部分 コードの削除です エディタビューコントローラーは UINavigationItemRenameDelegateに従います しかしこれはもう必要ありません UIDocumentがすべての名前の変更を 自動的に行います ですのでデリゲート定義 コードのあるデリゲートメソッド renameDelegateの割当を すべて削除します
次にその他のnavigationItem カスタマイゼーションを削除します 「style」と「backAction」はどちらも ドキュメントビューコントローラーにより 自動的に構成されるので これを完全に削除することができます
UIDocumentPropertiesオブジェクトを 作成するために使用される updateDocumentPropertiesメソッドも あります このメソッドはさまざまな場所から 呼び出されます しかしこれも必要がなくなりました UIDocumentViewControllerが これをすべて行ってくれるので これとすべてのコールサイトを削除できます これですべて完了です エディタビューコントローラーは アプリに固有の機能のみに 対処します ドキュメント管理の 基本的なタスクの管理や ナビゲーションバーのデフォルト構成は 行わなくなります 代わりにアプリに固有な重要な要素に 集中できるようになります ドキュメントベースのアプリを 次のレベルに引き上げ ユーザーにすばらしい体験を 提供するための情報は以上です
UIDocumentを使用するために データモデルを変換します 次にコンテンツビューコントローラーを 変換して 新しいUIDocumentViewController ベースクラスを使用します その後 ビューコントローラーを確認し 必要のなくなったコードをすべて削除します ご視聴ありがとうございます ♪ ♪
-
-
3:54 - Loading a document
override func load(fromContents contents: Any, ofType typeName: String?) throws { // Load your document from contents guard let data = contents as? Data, let text = String(data: data, encoding: .utf8) else { throw DocumentError.readError } self.text = text }
-
4:08 - Saving a document
override func contents(forType typeName: String) throws -> Any { // Encode your document with an instance of NSData or NSFileWrapper guard let data = self.text?.data(using: .utf8) else { throw DocumentError.writeError } return data }
-
4:34 - Manually saving and loading a document
override func save(to url: URL, for saveOperation: UIDocument.SaveOperation, completionHandler: ((Bool) -> Void)? = nil) { self.performAsynchronousFileAccess { // Set up file coordination and write file to URL } } override func read(from url: URL) throws { // Set up file coordination and read file from URL }
-
5:08 - Defining document that require saving
class Document: UIDocument { var text: String? { didSet { if oldValue != nil && oldValue != text { self.updateChangeCount(.done) } } } }
-
6:30 - Updating the view hierarchy for a document
override func documentDidOpen() { configureViewForCurrentDocument() } override func viewDidLoad() { super.viewDidLoad() configureViewForCurrentDocument() } func configureViewForCurrentDocument() { guard let document = markdownDocument, !document.documentState.contains(.closed) && isViewLoaded else { return } // Configure views for document }
-
7:17 - Updating navigation items for a document
override func navigationItemDidUpdate() { // Customize navigation item }
-
8:01 - Manually opening a document
documentController.openDocument { success in if success { self.present(documentController, animated: true) } }
-
9:20 - Renaming a UIDocument without UIDocumentViewController
navigationItem.renameDelegate = document
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。