-
Xcode PreviewsによるプログラマティックなUIの構築
SwiftUI、UIKit、または AppKit で書かれたUIコードを素早く反復するために、Xcode 15で#Previewマクロを使用する方法を学びます。キャンバス内でビューと対話するためのユニークなワークフローを考えます。同時にUIの複数のバリエーションを表示する方法を見つけ、エントリ間の遷移をテストするためにウィジェットのタイムラインを数秒で移動する方法を説明します。また、ライブラリにプレビューを追加し、サンプル アセットを提供し、物理的なデバイスでビューをプレビューして、その機能と既存のデータを活用する方法も紹介します。
関連する章
- 1:26 - What are previews
- 3:42 - Preview syntax basics
- 4:44 - Writing SwiftUI previews
- 5:50 - Writing UIKit & AppKit previews
- 6:08 - Demo: Putting writing previews into action
- 11:39 - Writing previews for widgets
- 15:58 - Previewing in library targets
- 20:28 - Passing sample data into previews
- 22:08 - Previewing on devices for full fidelity and access to data
- 25:50 - Wrap-up
リソース
関連ビデオ
WWDC23
-
ダウンロード
こんにちはKevinです プレビューを担当しています ソフトウェア開発 特にアプリの構築は 反復的かつ創造的な プロセスなので コードをテストし実現したものを チェックする 迅速な方法が必要です そこでプレビューを開発 ほぼ瞬時に視覚的なフィードバックを 得ることが可能で クリエイティブなことに 集中できるようになります プレビューは柔軟に構築されています さまざまな種類のビューやデータ デバイスを含む アプリ全体で利用できます プレビューを初めて使用する場合でも しばらくプレビューを使用していても プレビューを最大限に活用できるかと 思います 3つの部分に分けてみます まずプレビューとは何かを確認 プレビューがどのように機能するか プロジェクト内のコードと どう関連するかの理解に有用です 次にプレビューにコンテンツを 提供する2つの異なる方法 ビューとウィジェットについて これらとXcodeでの独自ワークフローを 記述する方法を説明します 最後にプロジェクトに プレビューを導入し始めると 必ず遭遇する一般的なシナリオや 質問についてです プロジェクトでプレビューを進めるのに 役立つヒントご紹介します では始めましょう まずプレビューとは何でしょうか プレビューはビューを構成する コードのスニペットです ソースファイルの最上位に 記述されますので 型や関数の内部にネスト されていないことになります それほどシンプルなのです #Previewマクロを使用して ビューを返します プレビューは コードの残りの部分や リソースと一緒にアプリに コンパイルされます プレビューはこれらのシンボルや リソースにアクセスできる 非常に柔軟な仕様となっています アプリ内の任意のプレビューを 自由に設定できるうえ Xcodeのキャンバスに 直接表示されます プレビューの目的のひとつに反復処理を 高速化することもあります プロジェクト内のSwiftコードを 編集すると Xcodeは2つのことを 自動的に実行します まず変更点を精査して 最小限のコードを再コンパイル 続いてプレビューを再実行します つまりコードの作成と反復に 集中して Xcodeが自動的にビルドと実行 プレビューの更新も行います プレビューを定義したら Xcodeは追加のコードを 記述することなく そのプレビューをさまざまな コンテキストで自動実行可能 たとえばダーク モードでのテストや さまざまな文字サイズなどでも これは完璧な例えではありませんが プレビューをテストのように考えると わかりやすいかもしれません テスト同様プレビューでは プロジェクトのコードを実行 コードをエミュレートするわけでは ありません テストおよびプレビューしている内容が ユーザーが体験するものを 反映していることを意味します 次にテストとプレビューの 作成に着目すると 開発の迅速化に役立ち ほんの少しでも大きな効果が アプリのレイヤーをテスト後に プレビューすることも可能です たとえばテストではアプリの機能の 重要箇所を実行する 高レベルのUIテストを行うことが可能で 同時に個々のコンポーネントの 単体テストの作成も可能です 同様に大部分のプレビューを 作成したり 個々のリーフビューのプレビューも 作成できます 以上がプレビューの仕組みの概要です 続いて作成方法とプレビューできる コンテンツの種類に焦点を当てます 何をプレビューするかに関係なく 定義するすべてのプレビューは 3つの基本形状を持ちます ソースファイルの 最上位にあるプレビュー マクロ initializerから始めます 次にコンテンツの末尾のクロージャを 1つ以上返します ここでプレビューしたい シナリオに合わせて UIを構成できます プレビューを作成するために 必要なのはこれだけですが オプションでプレビュー自体を 構成することも可能で さらに柔軟性を高めます 名称設定して プレビューの内容に応じて initializerで追加の構成を渡す 必要がある場合の例 これをいくつか見ていきます プレビューできるコンテンツの 種類について説明しましょう ビューとウィジェットの2種類が メインです ビューはSwiftUIやUIKit またはAppKitから取得できます SwiftUIの場合は作業中の ビューを返すだけです ただし作業中のビューを ただ渡す必要はなく 必要な他のビューへの配置が可能です 多くの場合広いコンテキストで ビューを操作する必要があるため これは有用です たとえば常にリスト内に存在する ことを意図したビュー
ここは必要な場合にビューで 環境データを提供するmodifierを プレビューする場所でもあります プレビューはアプリの最上位で 定義するシーンのようなものです シーンはアプリへのエントリポイントとして 機能します データを設定して それをビューに渡します プレビューも同じ目的を果たすため データとアセットを同様に 設定することで プレビューしているビューに 渡すことが可能です プレビューを構成する際には 名称の設定もできます SwiftUIのようなビューベースの プレビューは 複数の構成特性を可変引数リスト として渡すことをサポートしています たとえばプレビューしている デバイスの開始方向を設定 APIの形式も UIKitとAppKitで共通です SwiftUIビューの代わりに ビューコントローラーを作成し 必要に応じて構成するだけです ビューコントローラー以外にもUIViewまたは NSViewを直接プレビューすることも可能です つまり構築しようとしているものに応じて 多くの柔軟性があるのです プレビューの使用を開始する方法を 理解していただくために これまで説明した内容や プレビューを柔軟にする ツールをいくつか ご紹介したいと思います 画像をコラージュするアプリを 作成中です 写真を選んで レイアウトを選択して フィルターを追加できます Xcodeに移動してプレビュー キャンバスの機能を調べてみましょう 画像にフィルターを追加できる ビューの作成を開始しました このビューを繰り返し使用するのに プレビューが必要です まずエディタの右上に移動して オプションメニューをクリックし キャンバスモードが有効に なっていることを確認します 新しいプロジェクトではキャンバスモードが デフォルトで有効ですが 既存のプロジェクトの場合は 手動でオンにする必要がある場合も キャンバスモードが有効になっていても プレビューが定義されていない限り キャンバスは非表示のままなので プレビューを追加します #Previewと入力し始めると Xcodeがプレビューを提案してくれます 完了を受理するとXcodeがプレビューを ビルドして実行し ビューがキャンバスに直接表示されます ビューの作業に使用できる 3つのモードが キャンバスの左下隅に表示されます デフォルトのモードはライブモード またはインタラクティブです これらのスライダーをドラッグするなど キャンバス内のビューの操作は ほとんどこのモードままにしています
アニメーションをテストしたり 非同期コードの呼び出しと 応答も行えます 2番目のモードは選択モード つまり静的モードです
このモードではビューの スナップショットを取得し キャンバス内の要素を操作 できるようにします ビューをクリックするとソースエディタで そのビューを作成した コード行が強調表示されます このラベルのような特定のテキスト ビューをダブルクリックすると フォーカスがソースエディタに 移動するのですぐに変更できます Vignetteはより簡潔なラベルです 環境についてお話します プレビュー環境のことです キャンバスにはライトモードで プレビューが表示されていますが ダークモードで表示したい場合は どうすれば? コードを編集して 配色を設定することもできますが 多くの場合ダークモードで すぐに確認して 調整を加えたいこともあるでしょう そこでキャンバスのデバイス設定 ポップオーバーを使用します 下部のバーでコントロールアイコンを クリックして設定を表示 ダークモードまたは特定の 動的タイプのサイズを有効化 両方の配色でビューが どのように見えるかを 同時に確認したい場合は どうすればいいでしょうか? その際はプレビューの3番目のモードで ある可変を使用します
キャンバスの下部にある 可変モードをクリックして 値を表示したいデバイス設定を 配色や 動的タイプのサイズを選択 個々の可変をクリックして検査し 各可変をページ移動できます ふーむ このように大きな動的タイプの サイズになると ビューの乱れが目立ちます 修正しましょう VStackの代わりにコントロールの グループに最適なフォームを使用します コントロールをセクションに配置すると フォームの見栄えが良くなります 各HStackをセクションに設定します すべてのHStackインスタンスで この変更を行うには マルチカーソル編集を利用します 最初のHStackを選択して Command + Option + Eで カーソルを見つけて HStackのインスタンスごとにに挿入 これをそれぞれのセクションで変更します
各セクションのヘッダも 2番目の末尾クロージャに 付与します 下矢印をクリックして末尾の クロージャーを追加
ヘッダにある既存のラベルを 使用します 矢印でラベルに戻り Command + Option + 右中括弧で ラベルをヘッダに移動します ビューがかなり改善されました これらの機能はすべてAppKitおよび UIKitビューとビューコントローラーでも 機能します CoreImageフィルタをレンダリングする ビューコントローラーにタブを切り替え キャンバスをライブモードに戻します このビューコントローラーの プレビューは作成済みですが SwiftUIのものと 似通ったものとなっています プレビューマクロでは ビューコントローラーに サンプルイメージを渡しました フィルタを適用してこの画像を テストしたいので フィルターをビューコントローラーに 渡すコードを追加します ビネット効果って 決して古くなりませんよね ビューコントローラーを構成する以外に プレビューを構成することもできます どのプレビューでも最初の引数として オプション名を指定できます SwiftUIやUIKitなどのビュー プレビューを使用すると 名称の後の可変引数リストに 複数の特性を追加できます たとえばプレビューを横向きで 開始するように設定 ビューのプレビューの作成に 関する簡単な紹介でした
プレビューできるコンテンツの 別のカテゴリはウィジェットです ウィジェットでプレビューが いかに高速であるかがわかります プレビュー可能なウィジェットは 2種類あります まず個々のエントリを生成するタイムライン プロバイダを使用するウィジェット Xcodeではタイムラインプロバイダ 全体をプレビューすることも プレビュー内でエントリの独自の タイムラインを作成することも可能 Xcodeに移動して それぞれの例を見てみましょう この画像コラージュアプリには 1時間ごとにランダムに作成された コラージュを表示するタイムライン プロバイダ付きウィジェットがあります タイムラインプロバイダについては 次の3つの項目でプレビューを作成 まずプレビューしたいウィジェット 2番目にタイムラインプロバイダ 3番目はプレビューに使用する ウィジェットファミリです
コードは少な目ですがXcodeで いくつかの優れたワークフローを 提供します このウィジェットは1時間ごとに ランダムなコラージュを構築しますが タイムラインのすべてのエントリを見るのに 1時間も待つ必要はありません 各タイムラインエントリの スナップショットを プレビューしてキャンバスに表示します クリックして移動するか 矢印キーを使用します Xcodeがウィジェットと通信し エントリ間の遷移を アニメーションで表示 ユーザーインターフェイスだけでなく 不具合の検知も可能ですが タイムラインの別時点間の 変化は対象外です たとえばこのエントリー8と9の間の アニメはあまり良くなく クロスフェードするだけだ このシナリオを修正したいものの タイムラインプロバイダがランダムなので テスト中にこのシナリオが再表示 されるかどうかはわかりません ここで特定のエントリの タイムラインが役に立ちます 繰り返したい正確なシナリオを 作成できます プレビューのtimelineProvider:を 単純タイムラインに変更し タイムラインプロバイダを 返す代わりに 修正したいケースを再現する 2つのエントリを返します ただしこの移行のために修正の必要が あるコードは別のファイルに存在するため 別のファイルに移動するときに このプレビューを保持したい ピン留めを使用するとプレビューを キャンバス内に保持できます キャンバスの左上にある ピンボタンをクリックすると 別のファイルに移動してもプレビューが アクティブなままになります
これはコラージュを描画し 遷移を含むビューです 問題の解決に集中できるように キャンバス内でタイムラインの 再生ボタンと ループボタンを押します コードの修正中Xcodeは この遷移を連続再生します ここが問題が コラージュが行で構成されている場合は 遷移を追加しますが 列で構成されている場合は 遷移を追加しません 遷移をコピーして貼り付けます それ自体良いのですが 後続が横から入ってきます 下から表示した方が良いでしょう できました プレビューでタイムライン プロバイダを使用して 迅速に反復処理を行うだけでなく 特定のイベントでアニメーションを 微調整することもできます これによりUIの構築と反復が より速く楽しくなります
これらのウィジェットワークフローは プレビューできる 2つの種類のウィジェットである ライブアクティビティでも使用できます APIはほぼ同じに見えますが タイムラインプロバイダと エントリを提供する代わりに 一連のライブアクティビティ属性と 一連の状態を指定します 以下に例を示します まずinitializerで使用する 属性を渡します 次にそれらの属性のコンテンツ 状態を渡します たとえばピザを注文するための ウィジェットを構築している場合 カスタムの状態セットを提供して ピザの焼き方と配達の様子を確認し すべての状態間のアニメーションを テストします これはウィジェットのプレビューで できることのほんの一部です 詳細は「Bring Widgets to Life」 セッションをご覧ください この講演の最後の部分に進むにつれて プロジェクトでのプレビューの作成を 最大活用するためのヒントを プロジェクトのセットアップや データの提供 デバイスの機能を活用していきます まずライブラリターゲットのコンテンツの プレビューについて説明します これにはフレームワークとSwiftパッケージ または動的ライブラリが含まれます ライブラリを使用する理由は いろいろです ライブラリを使用してプロジェクトを モジュール化している場合や 他の人に配布するライブラリを 開発している場合があります プレビューはこれらのターゲットで 機能しますが 特筆すべきはライブラリ ターゲットを 活用できることで 任意のプロジェクトでさらに プレビューを実行できます ライブラリを活用する最初のステップは プレビューがコードを実行するために 使用する実行可能ファイルを 理解することや プレビューでレンダリングするには 実行可能ファイルやウィジェットが必要です 通常アプリでは行えますが アプリがない場合はどうすれば? プレビューはどの実行可能ファイルを 使用するか3点で決定します 1つは作業中のソースファイル 2つ目はそれらのファイルを含む ターゲットとすべての依存関係 3番目にプレビューは選択した スキーム内のターゲットと ターゲットの依存関係を インターセクトします プレビューではアクティブなスキームにある アプリのみが選択されます いくつか例を見てみましょう 最も単純なケースでは ターゲットのメンバーである 単一のソースファイルで 作業している可能性です これがプレビューに使用されることに 何の不思議もないでしょう もしソースファイルが 2のターゲットに ある場合はどうなるでしょうか たとえば試用版と完全版などが それに該当します ここでこスキームの出番です プレビューではアクティブなスキームにある アプリのみが使用されます 別の例を次に示します 2つのSwiftファイルを開いていて それぞれ別パッケージで インポートされる可能性があります 属性があると仮定します これらのファイルをたどって 最初の共通実行可能ファイルを見つけます それを念頭に置いて最初の 質問に戻りましょう アプリがない場合はどうすればよいのか? この場合プレビューは ライブラリをロードする XCPreviewAgentを ユーザーに代わって作成します 自動的に作成されますが これがどのように機能するかを 特にプロセス名称を知っておくと役立ちます これによりコードがどこで 実行されているかわかるからです たとえばXCPreviewAgentの クラッシュレポートがある場合 それがコード内で発生していることや 問題の場所を見つけることが可能です ただしライブラリ ターゲットを 利用すると少なくとも 2つの方法でプロジェクト内での プレビュー動作を向できます このことを詳しく見ていくことも できますが ここでは簡単に触れて おきたいと思います まずアプリをライブラリに モジュール化すると より小さなスキームを作成して ビルド時間を短縮できますし プロジェクトの一部分に 集中することもできます ターゲットのサブセットを含む 小規模なスキームを使用している場合でも プレビューの機能を最大限に活用できます 次にコードをライブラリに モジュール化すると アプリターゲットが提供していた Info.plistキーを必要とするビューが 存在する可能性もあります プレビュー専用のアプリを作成することで このビューをプレビューすることもできます やり方は以下のとおり 特定のInfo.plistキーが 必要なPhotoライブラリを 使用するビューを作成しているとして ビューをライブラリに保存しました 名称は SamplePhotoLibraryUtilities
新しいターゲットを作成すると 適切な機能でプレビューできます 次に必要な機能を追加します この場合Info.plistキーを 追加する必要があるため ビルド設定に移動します 写真ライブラリの使用文字列を フィルタして設定し 次に作業中のビューを含む ライブラリがアプリに 埋め込まれていることを確認 タブで ターゲットの属性として追加 ファイルのコピーフェーズで 埋め込みます これでこのプレビューアプリと ライブラリのみを含むスキームを 選択する準備ができました ビューをプレビューすると プレビュー用に作成したアプリに 適切なInfo.plistキーが すべて配置されます これにより作業中のすべてのビューを プレビューする機能を維持しながら より短いビルド時間で小規模な スキームを利用できるようになります 次にデータとアセットをプレビューに 取り込む方法について説明します これは以前のデモに組み込み済みですが 戻って作業内容を 確認したいと思います フィルタをレンダリングする 前に見ていたビュー コントローラーに戻りましょう プレビューを構成するときに サンプル画像を渡しました この画像はプロジェクトのアセット カタログから取得したものです ナビゲーターを表示すると アセットカタログが プレビューのコンテンツフォルダ内に 見えます これらの画像は開発中にさまざまな シナリオをテストするのに役立ち テストするすべてのデバイスを Photosで構成する必要がなくなります 自分のアプリに搭載したくない場合 Development Assets機能を 使用すると回避できます これらはビルド設定で構成した プロジェクト内のフォルダについては App Storeに提出する際 アプリから削除されます これにはアセットカタログやプレビューに 使用するリソースが含まれる場合も コンテンツフォルダをDevelopment Assets パスとして追加しましょう
プロジェクト設定でビルド 設定タブに移動すると 開発アセットをフィルタできます ダブルクリックして編集した後 パスを手動で入力することも コンテンツフォルダをポップオーバーに ドラッグすることもできます
これを追加するとApp Storeに 送信するときにこのパスが アプリから削除されます 新しいプロジェクトまたは ターゲットを作成すると Development Assetsパスが 自動的に設定されますが 追加のパスを追加したり 他のターゲットタイプや プロジェクトへの追加も可能です このデモではDevelopment Assetsを使用して プロジェクトにアセットを追加することで プレビュー用のアセットを 提供する方法を説明します これはすべてのアセットを すべてのデバイス間や チーム間で共有したい場合に最適です すぐに起動して実行したい場合は データと画像を提供する 別の方法があります デバイス上にアセットやデータが ある場合がほとんどです プレビューを使用するとそれらも 最大活用できます Xcodeのシミュレータは 多くの機能を備えているため 開発の出発点となります プレビューはシミュレーターでも 物理デバイスでも適切に機能します アプリが最終的に出荷される デバイスでプレビューしたい場合 たとえばカメラやセンサーに アクセスしたいとします 別のケースはデバイスに写真や ファイルなどの実際のデータが 大量に保存されている 可能性があることです デバイスのデータを活用する方法の 例をお見せします
作業中の別のビューにある iCloudの写真ライブラリから 写真を選択できるボタンから 次にレイアウトを選択して 新しいコラージュを作成 他のビューと同じ方法で これをテストしたいのですが 豊富な写真を選択できる デバイスを使用する必要があります キャンバスの下部にある プレビューデバイスピッカーで プレビューに使用する デバイスを変更します いくつかオプションを見てみましょう ほとんどの場合 自動モードを使い続けられますが これにより選択した実行先の デバイスファミリが追跡されます メニューの反対側には 「その他」サブメニューがあります ウィンドウに追加した シミュレーターデバイスがリストされるので 必要なモデルを正確に選択できます 機能だけでデバイスを 選びたい場合もあるでしょう メニューの中央では共通の 機能ごとにデバイスが提供されるので どのモデルにTouch IDが搭載 されているかを思い出す必要はありません このデバイスを傍らで使用して ライブラリから大量の写真にアクセス できるようにしたい場合
デバイスピッカーにはMacに接続 されているすべてのデバイスも含まれます これらの接続されたデバイスの 1つを選択すると Xcodeはシミュレーターを 完全にバイパスし デバイス専用にビルドしてプレビュー 実際のデバイス上でビューを 実行できるようになりました ただし自分のデバイスだからといって これまで説明してきたプレビューの機能を 利用できないわけではありません キャンバスのモードを引き続き使用でき デバイス設定を行うこともできます たとえばダークモードでは どのように見えるでしょうか
コードの更新はすぐに デバイスに表示されます このビューにはナビタイトルが 必要なので コードにNavigationTitleを追加し タイトルをカスタマイズすれば すぐにデバイス上でレビューできます
速いですね! これで機器上の画像全部と 写真ライブラリの統合を テストする準備ができました 「Photosから追加」をタップし 写真を選択して かわいいリスですね 「追加」をタップします ビューの機能は素晴らしい 写真がレイアウトピッカーに表示されるので レイアウトをすぐにテストできます いいですね Xcodeで編集してビューが 反映される瞬間がたまらない 以上がプロジェクトでプレビューを使う 3つのコツでした まとめます プレビューAPIを利用すると アプリ全体でUIプレビューを 柔軟に定義でき 全プラットフォームに対応 SwiftUIとUIKitにAppKit 複数の種類のウィジェット プレビューも柔軟なので 必要に応じて プレビューを正確に構成できます サンプル データとアセットを渡し コードで環境をセットアップし Xcodeの機能を利用してさまざまな デバイス設定をテストできます またプレビューにより さまざまなデバイスで柔軟に プレビューできるようになり キャンバス内で直接シミュレーターを 使用しても デバイスを使用して 再現度合いが高いデータや 写真へのアクセスが可能です どのデバイスを選択してもプレビューの すべての機能を利用できます 最後にアプリ一部をライブラリに モジュール化すると Xcodeでスキームを作成して ビルド時間を短縮して 作業に集中することが可能です プレビューは創造性を発揮 するのに最適です ありがとうございます
-
-
1:30 - Basic preview
#Preview { MyView() }
-
5:05 - Previewing a SwiftUI view in a list
#Preview { List { CollageView(layout: .twoByTwoGrid) } .environment(CollageLayoutStore.sample) }
-
5:37 - Previews can have a name and configuration traits
#Preview(“2x2 Grid”, traits: .landscapeLeft) { List { CollageView(layout: .twoByTwoGrid) } .environment(CollageLayoutStore.sample) }
-
5:58 - Previewing UIKit view controllers and views
#Preview { var controller = SavedCollagesController() controller.dataSource = CollagesDataStore.sample controller.layoutMode = .grid return controller } #Preview(“Filter View”) { var view = CollageFilterDisplayView() view.filter = .bloom(amount: 15.0) view.imageData = … return view }
-
7:08 - Xcode can help suggest a preview
#Preview { FilterEditor() }
-
11:30 - Setting a UIKit preview to start in landscape
#Preview("All Filters", traits: .landscapeLeft) { let viewController = FilterRenderingViewController() if let image = UIImage(named: "sample-001")?.cgImage { viewController.imageData = image } viewController.filter = Filter( bloomAmount: 1.0, vignetteAmount: 1.0, saturationAmount: 0.5 ) return viewController }
-
12:20 - Previewing a small widget with a timeline provider
#Preview(as: .systemSmall) { FrameWidget() } timelineProvider: { RandomCollageProvider() }
-
25:07 - Updating the navigation title while previewing on device
.navigationTitle(“Add Collage”)
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。