WWDC Quick Look 💓 By SwiftGGTeam
Structure your app for SwiftUI previews

Structure your app for SwiftUI previews

元の動画を見る

ハイライト

Apple は固定プレビューを使用してデモを行っています。@StateObject、開発アセット、単純な入力、およびプロトコル抽象化により SwiftUI ビューがリファクタリングされ、Xcode プレビューで重い起動ロジックをスキップして実際のインターフェイスの状態をオーバーライドできるようになります。

主要内容

SwiftUI プレビューが失敗する場合、一般的な根本原因は、ビューがキャンバスにドラッグするランタイム依存関係が多すぎることです。修飾子のエラーを見つけやすくなります。ネットワーク モデルを開始するにはサムネイル プレビューが必要で、CloudKit レコードを構築するにはエディタ プレビューが必要ですが、ステータス バーのプレビューがありませんEnvironmentObjectレンダリングできません。このようなビューはアプリ内で実行できますが、プレビューですぐに試行錯誤するのは困難です。

このセッションでは、プレゼンテーション全体で写真コラージュ アプリを使用します。 Kevin は最も軽いワークフローから始めます。つまり、プレビューを固定し、編集中のファイルに戻ります。このようにして、開発者は、サムネイルの色、アセット カタログのダーク モードの色、および動的フォント サイズを変更するときに、実際の使用シナリオを離れる必要がありません。

次に、セッションは問題をアプリ構造に進めます。 SwiftUI アプリのライフ サイクルにより、アプリのスタートアップ エントリも SwiftUI タイプになりますが、プレビューではリーフ ビューの完全なネットワーク層を初期化すべきではありません。@StateObjectここでの価値は、SwiftUI に高価なモデルの作成を管理させ、現在の画面に必要のない初期化作業をプレビューにスキップさせることです。

最後に、Apple はビュー入力設計にプレビュー機能を取り入れています。単純な不変値を渡すことができる場合は、CloudKit をサポートするモデル全体を渡さないでください。値を変更する必要がある場合は、使用しますBinding;複雑なデータを引き渡す必要がある場合は、プロトコルの抽象化と設計時モデルを使用します。本当に層間転送に適した環境で使用してください。こうすることで、実際の CloudKit データ モデルを起動せずに、アプリ プロセス全体をキャンバスで実行できます。

詳細

コンテキストを保持したピンのプレビュー

(02:31) 最初のデモは、特定の問題を解決します。レイアウトのサムネイルだけを見ると、灰色の四角形は正常ですが、写真コンテンツをスライドさせてセレクター上に置くと、灰色の四角形が消えます。 Xcode では、セレクターのプレビュー ピンを押したままにして、サムネイル ファイルに戻って編集を続けることができます。

注: このセッションのリソースとコード スニペットは両方とも 0 です。以下のすべての Swift コード ブロックは、構造と API の使用法を表現するためにトランスクリプト内のデモンストレーション ステップに基づいてコンパイルされた最小限の例であり、[Apple Code] タブの元のスニペットではありません。

struct LayoutThumbnail_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            LayoutThumbnail()
                .previewDisplayName("Default")

            LayoutThumbnail()
                .environment(\.colorScheme, .dark)
                .previewDisplayName("Dark")
        }
    }
}

キーポイント:

  • Group複数のプレビューをトランスクリプトにコピーする操作に対応し、デフォルトの見え方とダークな見え方を同時に確認しやすくなります。 -environment(\.colorScheme, .dark)インスペクタでの暗い見た目を切り替える目的に対応し、コードで修正して繰り返し状態を返すことができます。 -previewDisplayNameキャンバス内で複数の設定を区別できるようにし、ピン留めされたプレビューでコンテキストを検査するのに適しています。

(05:15) 動的フォントの例では、サムネイルは環境からサイズ カテゴリを読み取り、アプリ独自に定義されたスケールを使用してサイズと間隔を調整します。この種の問題は、特定の値を固定するだけでは解決できません。同じ小さなビューを複数のアクセシビリティ サイズで同時にチェックする必要があります。

struct LayoutThumbnail: View {
    @Environment(\.sizeCategory) private var sizeCategory

    var body: some View {
        RoundedRectangle(cornerRadius: 6)
            .frame(
                width: 44 * sizeCategory.previewScale,
                height: 44 * sizeCategory.previewScale
            )
    }
}

キーポイント:

  • @Environment(\.sizeCategory)トランスクリプトにおける「環境を介してサイズ カテゴリを渡す」という実践から。 -previewScaleこれは、ビュー内に複数のマジック ナンバーのセットが散在することを避けるために、アプリによって独自のレイアウト用に定義された拡張機能です。
  • サイズと間隔を同じ縮尺に接続した後、固定されたアクセシビリティ プレビューによってレイアウトの問題がすぐに明らかになります。

使用@StateObjectプレビュー起動の手戻りを回避する

(06:11) アプリの起動時の一般的な初期化には、ドキュメントの読み取り、CloudKit への接続、デバイスへのアクセス、またはネットワーク データの取得が含まれます。プレビューの利点は、小さなビューに直接ジャンプできることです。このシーンは開始コストの全額を負担すべきではありません。

@main
struct MosaicApp: App {
    @StateObject private var networkModel = NetworkModel()

    var body: some Scene {
        WindowGroup {
            RootView()
                .environmentObject(networkModel)
        }
    }
}

キーポイント:

  • トランスクリプトでは、最初にアプリ タイプ属性にネットワーク モデルを設定し、次にそれを次のように変更します。@StateObject
  • @StateObjectSwiftUI にモデルのライフサイクルを処理させ、変更により UI の更新を促進させます。
  • セッションにはデバッグ プレビューとデバッグ ゲージが明確に表示されます。変換前はプレビューによって CPU とネットワークの作業がトリガーされ、変換後は現在のプレビューに必要のない起動作業が行われなくなります。

サンプル データを開発資産に配置する

(09:22) 写真アプリでは、レイアウトと効果をテストするために大量のサンプル画像が必要ですが、これらの画像はリリース パッケージに含めるべきではありません。 Xcode ターゲットの開発アセットは、フォルダー パスを開発バージョンのみとしてマークすることをサポートしており、アセット カタログと Swift ファイルの両方をそこに配置できます。

protocol CollageSlot {
    associatedtype ImagePublisher

    var imagePublisher: ImagePublisher { get }
    var effects: SlotEffects { get set }
}

struct DesignTimeSlot<ImagePublisher>: CollageSlot {
    var imagePublisher: ImagePublisher
    var effects: SlotEffects
}

struct DesignTimeCollage<ImagePublisher> {
    var name: String
    var layout: CollageLayout
    var slots: [DesignTimeSlot<ImagePublisher>]
}

キーポイント:

  • トランスクリプトのプレビュー コンテンツ フォルダーには、プレビュー イメージと 2 つの Swift ファイルが含まれています。 -DesignTimeCollageそしてDesignTimeSlotデモの設計時モデルに対応し、CloudKit ベースのモデルを置き換えるために使用されます。
  • 開発資産は境界のパッケージ化を担当し、ビューは引き続き通常の入力を通じてこれらのサンプル データを消費します。

ビューの入力をシンプルにする

(13:08) コラボレータ セルは当初、ObservedObjectコラボレーター、そしてコラボレーターのコンテンツのほとんどはCKUserIdentity。セルのプレビュー用に CloudKit クエリを作成する必要はありません。 Apple のアプローチは、まず UI が本当に必要とするものを調べ、次に入力を名前、画像、接続ステータスに減らすことです。

struct CollaboratorCell: View {
    let name: PersonNameComponents
    let image: Image
    let status: ConnectionStatus

    private static let nameFormatter = PersonNameComponentsFormatter()

    var body: some View {
        HStack {
            image
            Text(Self.nameFormatter.string(from: name))
            StatusBadge(status: status)
        }
    }
}

キーポイント:

  • 名前、画像、ステータスはすべて、トランスクリプトにリストされているセルの最小限の入力から取得されます。
  • セッションが最初に使用されますString単純な入力を表示し、それを次のように変更します。PersonNameComponentsシステムがロケールに従って名前の形式を処理できるようにします。 ・プレビュー CloudKitに依存しないため、オンライン、オフライン、ロングネーム、アバターなどの設定を素早く作成できます。

(17:47) ビューで値を変更する必要がある場合は、単純な型を渡します。Binding。インスペクターのサンプルは編集するだけで済みますSlotEffects、完全なイメージ スロット レコードを取得する必要はありません。

struct EffectsInspector: View {
    @Binding var effects: SlotEffects
    var replacePhotoHandler: () -> Void

    var body: some View {
        VStack {
            Slider(value: $effects.saturation)
            Button("Replace Photo", action: replacePhotoHandler)
        }
    }
}

struct EffectsInspectorPreviewHost: View {
    @State private var effects = SlotEffects()

    var body: some View {
        EffectsInspector(effects: $effects) {
            effects.saturation -= 0.1
        }
    }
}

キーポイント:

  • @Bindingトランスクリプトの中間部分に相当SlotEffects検査官のアプローチをパスします。 -EffectsInspectorPreviewHost「中間ビューが状態を保存し、バインディングをテスト対象のビューに渡す」に相当します。 -replacePhotoHandlerボタンの動作を呼び出し元に渡すセッション内のクロージャに対応して、プレビューはそれを使用して、ボタンが実際にトリガーされたことを確認できます。

プロトコルと環境の複雑な境界を処理する

(22:41) コラージュ エディターには、名前、レイアウト、スロット データが必要です。スロット イメージはクラウド、ディスク、またはエラー状態から取得される可能性があるため、Apple は次のように定義していますCollageProtocolスロット抽象化を使用して、エディターにコラージュ タイプとイメージ ピッカー タイプのジェネリックスを作成させます。

protocol CollageProtocol {
    associatedtype Slot

    var name: String { get set }
    var layout: CollageLayout { get set }
    var slots: [Slot] { get set }
}

struct CollageEditor<Collage: CollageProtocol, ImagePicker: View>: View {
    var collage: Collage
    var makeImagePicker: () -> ImagePicker

    var body: some View {
        EditorCanvas(collage: collage, makeImagePicker: makeImagePicker)
    }
}

キーポイント:

  • CollageProtocolトランスクリプトにおける「UI編集に必要なデータのみを記述する」という抽象化に対応。 -ImagePickerデザイン時の写真ピッカーに対応する汎用的なもので、プレビューで完全な写真ライブラリを開く必要はありません。
  • 実際のアプリ モデルとデザイン時モデルは同時にプロトコルに準拠でき、エディター コードはデータが CloudKit から来たのかプレビュー リソースから来たのかを知る必要がありません。

(27:34) 環境は、レイヤー間で状態を共有するのに適しています。クラウド同期ステータスビューの使用EnvironmentObjectしたがって、各プレビューはステータスを明示的に提供する必要があります。これには、オンライン、オフライン、最近の同期、その他のステータスが含まれます。

struct CloudSyncStatusView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            CloudSyncStatusView()
                .environmentObject(CloudSyncStatus.online)

            CloudSyncStatusView()
                .environmentObject(CloudSyncStatus.offline)

            CloudSyncStatusView()
                .environmentObject(CloudSyncStatus.lastSynced(hoursAgo: 2))
        }
    }
}

キーポイント:

  • environmentObject各プレビューを対応するトランスクリプトに添付しますCloudSyncStatus手術。
  • 通常のパスのみを検証することを避けるために、複数のプレビューでさまざまな同期状態をカバーします。
  • セッションでは、依存関係がビュー入力として定義できる場合は明示的な入力が推奨されることも思い出させます。環境は、実際に層間で渡す必要がある状態で使用されます。

重要ポイント

  • プレビュー マトリックスの作成: コア セルの明るい、暗い、アクセシビリティ サイズ、長いテキスト、空のデータ、その他の構成を作成します。実行する価値がある理由: ピン留めされたプレビューにより、小さなビューを実際のコンテキストに戻すことができます。開始方法: 一般的な状態を書き込むPreviewProviderGroup、各状態に追加しますpreviewDisplayName

  • 設計時モデルを解凍: CloudKit、コア データ、またはネットワーク モデルのプレビューのみを提供する軽量タイプのセットを作成します。実行する価値がある理由: セッションのコラージュ エディターは完全にインタラクティブですが、CloudKit を利用したモデルは作成されません。開始方法: まず、ビューが実際に必要とするプロトコルを定義し、次に実際のモデルとプレビュー モデルをそれぞれ準拠させます。

  • プレビュー アセットを開発アセットに移動: 十分な量の画像、JSON、または Swift サンプル データのセットを準備します。実行する価値がある理由: 開発アセットは開発バージョンに入るのみで、リリース パッケージのサイズは増加しません。開始方法: ターゲットの開発資産に追加するPreview Contentフォルダーを作成し、その中にプレビュー イメージとデザイン時の Swift ファイルを置きます。

  • インタラクティブ プレビュー用のホスト ビューを作成: テスト中のビューが必要な場合Bindingまたはコールバック、中央のビューを使用してそれを保持します@State。実行する価値がある理由: エフェクトインスペクターのスライダーと置換ボタンは、キャンバスとデバイス上のプレビューで直接操作できます。開始方法: 1 つ書いてくださいPreviewHost、バンドル$stateそしてテスト クロージャがターゲット ビューに渡されます。

  • 各ビューの入力境界を監査: リッチなモデルをできるだけ早く単純なタイプに変換します。実行する価値がある理由: 協力者セル 知る必要はありませんCKUserIdentity取得メソッドは、名前、アバター、接続ステータスを表示できれば十分です。開始方法: プレビューが難しいビューから開始し、UI が実際に読み取るフィールドをリストしてから、プレビューで使用されないモデルの依存関係を削除します。

関連セッション

  • SwiftUI ビューを視覚的に編集する — Xcode プレビュー キャンバスの視覚的な編集、環境切り替え、SwiftUI 反復ワークフローについて引き続き説明します。
  • SwiftUI のアプリの基本 - 説明AppSceneWindowGroupSwiftUI アプリのライフサイクルの基本はこのセッションで説明されます。@StateObject境界の先頭に追加されたコンテンツを開始します。
  • SwiftUIのData Essentials — システム説明@State@BindingObservableObject、このセッションのプレビュー データ フロー設計に対応します。
  • Xcode ライブラリにカスタム ビューとモディファイアを追加 — プレビュー キャンバスで使用するために、カスタム SwiftUI ビューとモディファイアを Xcode ライブラリに公開する方法を追加しました。

コメント

GitHub Issues · utterances