WWDC Quick Look 💓 By SwiftGGTeam
What's new in SwiftUI

What's new in SwiftUI

元の動画を見る

ハイライト

SwiftUI は、WWDC23 でクロスプラットフォーム機能の拡張を実現しました。新しいvisionOS シーン タイプ (WindowGroup、 Volume、ImmersiveSpace)、ObservableObject の代わりに @Observable マクロ、SwiftData ネイティブ統合、KeyframeAnimator および PhaseAnimator アニメーション API、および ScrollView のスクロール トランジションと位置合わせ機能です。

主な内容

SwiftUI が空間コンピューティングの時代に突入

以前は、3D インターフェイスを開発するには、まったく新しいフレームワークを学習する必要がありました。 visionOS のリリース後、このしきい値は破られました。

(01:55) SwiftUI は、visionOS で 3 つのシーン タイプをサポートします。WindowGroup2D ウィンドウとしてレンダリングされるこのコントロールには、深度に応じた 3D 効果が付いています。WindowGroupいつも通りお使いいただけますNavigationSplitViewTabViewなどの容器。

(02:26) はシーンを次のように宣言します.volumetricスタイルでは、ボリュームが得られます。 Volume は、ボード ゲームや建築モデルなどの 3D コンテンツを、限られたスペースに他のアプリケーションと並べて表示します。使用例Model3D静的モデルを表示するには、次を使用例します。RealityView照明とインタラクティブ性を備えた動的モデルを表示します。

02:59ImmersiveSpaceシーン タイプは没入型空間エクスペリエンスを定義し、混合とフルの 2 つの没入スタイルをサポートします。混合スタイルでは仮想コンテンツが現実の環境にオーバーレイされますが、フル スタイルではアプリケーションがユーザーの視野を完全に引き継ぐことができます。

watchOS 10 の新しいデザイン

(04:08) watchOS 10 はフルスクリーンのカラーデザインを特徴としています。NavigationSplitViewそしてNavigationStack新しいトランジションアニメーションを取得し、TabViewDigital Crown による垂直ページング スタイルを追加しました。

04:53containerBackgroundモディファイアは、プッシュおよびポップ時にアニメーション化されたトランジションを使用例して、ナビゲーションおよびタブ ビューの微妙な背景のグラデーションを構成します。containerBackgroundもサポートTabView

ウィジェットがより多くのシーンに登場します

(05:54) ウィジェットは、iPadOS 17 のロック画面、iPhone のスタンバイ モード、macOS Sonoma のデスクトップなど、より多くの場所に表示されます。さらに重要なのは、ウィジェットが対話型コントロールをサポートするようになったということです。

06:32ToggleそしてButtonアプリ内コードはアプリ インテントを通じてトリガーでき、ウィジェットは SwiftUI のトランジションおよびアニメーション修飾子を使用例してアニメーション化できます。

データ フロー革命: @Observable と SwiftData

10:44@Observableマクロは、SwiftUI データ フローへの最大のアップグレードです。クラスに追加@Observable、マークする必要はありません@Published, SwiftUI は、読み取られたプロパティを自動的に追跡し、実際に使用例されるプロパティが変更された場合にのみビューを更新します。

(13:16) SwiftData は、新しいデータ モデリングおよび管理フレームワークです。バンドル@Observableと置き換えます@Modelマクロでは、モデルは Observable の利点をすべて保持しながら永続化機能を獲得します。使用例Queryプロパティ ラッパーはデータベースからデータを取得し、データが変更されるとビューが自動的に更新されます。

##詳細

###visionOS シーン タイプ

(01:55)visionOS 上WindowGroupiOS の場合と同じ方法:

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

キーポイント:

  • WindowGroupvisionOS では 2D フラット ウィンドウとしてレンダリングされます。
  • ウィンドウ内のコントロールは、深度に応じた 3D 効果を自動的に取得します。
  • などのコンテナNavigationSplitViewTabView直接使用例可能

音量を設定する必要がありますvolumetricスタイル:

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .windowStyle(.volumetric)
    }
}

キーポイント:

  • .windowStyle(.volumetric)ウィンドウを 3D ボリュームに変える
  • 使用例するModel3D静的 USDZ モデルをロードする
  • 使用例するRealityView照明、物理学、インタラクティブ性を備えたダイナミックな 3D シーンを作成する

ImmersiveSpace の宣言方法:

@main
struct MyApp: App {
    var body: some Scene {
        ImmersiveSpace(id: "space") {
            ImmersiveView()
        }
        .immersionStyle(selection: .constant(.mixed), in: .mixed, .full)
    }
}

キーポイント:

  • ImmersiveSpace独立したシーンタイプです -.immersionStyleサポートされているイマージョン モードを指定する
  • 混合モードでは、コンテンツを実際の環境に重ね合わせます。
  • フルモードはユーザーの視野を完全に引き継ぎます

@監視可能なマクロ

(10:44) 以前に使用例されましたObservableObject各属性を追加する必要があります@Published

class DogModel: ObservableObject {
    @Published var dogs: [Dog] = []
    @Published var isFavorite: Bool = false
}

ここで 1 行だけ:

@Observable class DogModel {
    var dogs: [Dog] = []
    var isFavorite: Bool = false
}

ビューで使用例する場合、プロパティ ラッパーは必要ありません。

struct DogCard: View {
    let model: DogModel

    var body: some View {
        VStack {
            Text(model.dogs.first?.name ?? "")
            Toggle("Favorite", isOn: $model.isFavorite)
        }
    }
}

キーポイント:

  • @Observableマクロは監視可能な型に自動的に展開されます
  • 必要ありません@Published、保存されているすべてのプロパティはデフォルトで監視可能です
  • ビューでプロパティを直接読み取る必要はありません@ObservedObject- SwiftUI は実際に読み取られたプロパティのみを追跡し、未読のプロパティの変更は更新をトリガーしません。
  • 中間ビューを渡すときに不必要な更新はトリガーされません

SwiftData の統合

(13:16) 置く@Observableと置き換えます@Model、モデルは永続化されます。

import SwiftData

@Model
class Dog {
    var name: String
    var breed: String
    var spottedDate: Date

    init(name: String, breed: String, spottedDate: Date = .now) {
        self.name = name
        self.breed = breed
        self.spottedDate = spottedDate
    }
}

アプリでモデル コンテナーをセットアップします。

@main
struct DogWatcherApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: Dog.self)
    }
}

ビュー内のデータをクエリします。

import SwiftData

struct DogList: View {
    @Query(sort: \Dog.spottedDate, order: .reverse) private var dogs: [Dog]

    var body: some View {
        List(dogs) { dog in
            Text(dog.name)
        }
    }
}

キーポイント:

  • @Modelマクロは、Observable 機能と永続化機能の両方を提供します -.modelContainer(for:)シーンレベルでストレージスタックをセットアップする -@Queryデータベースからデータを自動的に取得し、データが変更されたときにビューを更新します -sortパラメータは並べ替えルールを指定します
  • フィルタリングやページングなどの高度なクエリをサポート

キーフレーム アニメーター アニメーション

19:07KeyframeAnimator複数属性の並列アニメーションをサポートします。

struct LogoAnimation: View {
    @State private var trigger = false

    var body: some View {
        KeyframeAnimator(
            initialValue: AnimationValues(),
            trigger: trigger
        ) { values in
            Image("logo")
                .offset(y: values.verticalTranslation)
                .rotationEffect(values.rotation)
        } keyframes: { _ in
            KeyframeTrack(\.verticalTranslation) {
                SpringKeyframe(30, duration: 0.25, spring: .smooth)
                CubicKeyframe(-50, duration: 0.3)
                SpringKeyframe(0, spring: .bouncy)
            }
            KeyframeTrack(\.rotation) {
                LinearKeyframe(.degrees(0), duration: 0.25)
                CubicKeyframe(.degrees(360), duration: 0.55)
            }
        }
    }
}

struct AnimationValues {
    var verticalTranslation: CGFloat = 0
    var rotation: Angle = .zero
}

キーポイント:

  • initialValueすべてのアニメーション化可能なプロパティが含まれます -triggerEquatable 状態であり、変更時にアニメーションをトリガーします
  • 最初のクロージャは、アニメーション化された値を使用例してビューを構築します。
  • 2 番目のクロージャは、時間の経過に伴う各属性の軌跡を定義します -KeyframeTrackサポートSpringKeyframeCubicKeyframeLinearKeyframe- 複数のトラックが並行して実行されます

フェーズアニメーターアニメーション

20:29PhaseAnimator一連のアニメーション ステージを順番に実行します。

struct WatchButton: View {
    @State private var sightingCount = 0

    var body: some View {
        Button {
            sightingCount += 1
        } label: {
            Image("dog-icon")
                .phaseAnimator(
                    [AnimationPhase.idle, .rotate, .grow, .settle],
                    trigger: sightingCount
                ) { content, phase in
                    content
                        .rotationEffect(phase.rotation)
                        .scaleEffect(phase.scale)
                } animation: { phase in
                    switch phase {
                    case .idle: .default
                    case .rotate: .spring(.snappy)
                    case .grow: .spring(duration: 0.3, bounce: 0.5)
                    case .settle: .spring(.bouncy)
                    }
                }
        }
    }
}

enum AnimationPhase: CaseIterable {
    case idle, rotate, grow, settle

    var rotation: Angle {
        switch self {
        case .idle: .zero
        case .rotate: .degrees(15)
        case .grow, .settle: .zero
        }
    }

    var scale: CGFloat {
        switch self {
        case .idle, .rotate: 1.0
        case .grow: 1.3
        case .settle: 1.0
        }
    }
}

キーポイント:

  • PhaseAnimatorステージを順番に実行し、前のステージが完了した後にのみ次のステージを開始します -trigger変更時にすべてのフェーズを最初から実行する
  • ステージごとに異なるアニメーションカーブを定義可能
  • 新春アニメ対応durationそしてbounceパラメータ -.snappy.bouncy.smoothデフォルトのスプリング構成です

ScrollView の改善

27:53scrollTransitionスクロール項目に視覚効果を追加します。

ScrollView {
    LazyVStack {
        ForEach(dogs) { dog in
            DogCard(dog: dog)
                .scrollTransition { content, phase in
                    content
                        .opacity(phase.isIdentity ? 1 : 0.5)
                        .scaleEffect(phase.isIdentity ? 1 : 0.8)
                }
        }
    }
}

キーポイント:

  • scrollTransitionアイテムが表示領域に出入りするときに効果を適用する -phase含む.topLeading.identity.bottomTrailing3つの州
  • 必要ありませんGeometryReader

ページネーションと配置:

ScrollView(.horizontal) {
    LazyHStack {
        ForEach(parks) { park in
            ParkCard(park: park)
                .containerRelativeFrame(.horizontal, count: 3, span: 1, spacing: 16)
        }
    }
    .scrollTargetLayout()
}
.scrollTargetBehavior(.viewAligned)

キーポイント:

  • containerRelativeFrameサブビューのサイズをコンテナに対して相対的にする -count画面をいくつかの部分に分割し、span占有するシェアの数を指定する -scrollTargetLayout調整する必要があるレイアウトにマークを付けます -scrollTargetBehavior(.viewAligned)スクロールが停止したときにビューの境界に揃える
  • もサポートしています.paging行動

触覚フィードバック

(22:03) 新しいsensoryFeedback修飾子:

Button("Record Sighting") {
    sightingCount += 1
}
.sensoryFeedback(.success, trigger: sightingCount)

キーポイント:

  • .success.warning.error事前定義されたフィードバック タイプを待つ -trigger変更時に触覚フィードバックを再生する
  • 触覚フィードバックを備えたすべてのプラットフォームをサポート

SFシンボルアニメーション効果

24:55symbolEffectモディファイアは SF シンボルにアニメーションを追加します。

Image(systemName: "slider.horizontal.3")
    .symbolEffect(.bounce, options: .repeat(1))

キーポイント:

  • .pulse.variableColor連続アニメーションです -.scale.appear.disappear.replace状態変化アニメーションです -.bounceイベント告知アニメーションです
  • 単一のシンボルまたはビュー階層全体に適用可能

重要なポイント

  1. visionOS 3D 表示アプリケーションを作成します

    • 使用例するWindowGroup + .volumetricスタイルショーケース製品3Dモデル
    • 使用例するModel3DUSDZ ファイルをロードすると、ユーザーはジェスチャーを使用例して回転したり表示したりできます
    • 入口:Model3D(named: "product")そして.windowStyle(.volumetric)
  2. 既存のアプリケーションにインタラクティブなウィジェットを追加

    • 既存の今日ウィジェットをインタラクティブ バージョンにアップグレードします。
    • 使用例するToggleそしてButton+ アプリインテントにより、ユーザーはウィジェットを直接操作できるようになります
    • 入口:#Preview(as: .systemSmall)プレビューウィジェット、AppIntentオペレーションを定義する
  3. @Observable を使用例してデータ層を再構築します

    • 既存のものを置くObservableObject + @Publishedモデルは次のように置き換えられます。@Observable- 大量の削除@ObservedObjectそして@EnvironmentObjectコード
    • 入口:置くclass Model: ObservableObjectに変更します@Observable class Model
  4. アニメーションを使用例してオンボーディング プロセスを作成する

    • 使用例するPhaseAnimatorステップバイステップのアニメーションを作成する
    • 各ステップが切り替わるときに、さまざまなシンボルアニメーションとビュー遷移がトリガーされます
    • 入口:PhaseAnimator(phases, trigger: step) + symbolEffect
  5. ScrollView 主導のギャラリーを作成します

    • 画像の横スクロール表示。各項目は画面幅の 80% を占めます
    • 使用例するscrollTransitionエッジアイテムを縮小して透明にする
    • 使用例するscrollTargetBehavior(.viewAligned)カード吸着効果を実現
    • 入口:containerRelativeFrame(.horizontal, count: 5, span: 4)

関連セッション

コメント

GitHub Issues · utterances