ハイライト
SwiftUI のデータ フローはその中心的な設計コンセプトであり、初心者が最も混乱しやすい場所でもあります。セッション 「データ依存関係グラフ」の概念に基づいて、システムはすべてのデータ フロー関連の属性ラッパーを分類します。
主要内容
View SwiftUI の構造は存続期間が短いです。上位レベルの状態が変化するたびに、SwiftUI はビュー構造体を再作成し、それを使用して新しいインターフェイスを生成することがあります。開発者がデータ ライフ サイクルをビュー構造体のライフ サイクルとして理解している場合、更新、ナビゲーション、マルチウィンドウ、および非同期読み込み中に状態の損失が発生することになります。
このスピーチでは、このビューにはどのようなデータが必要なのか、データは変更されるのか、データはどこから来たのかという固定的な質問が行われます。上位層から渡されたデータのみを表示する場合は、通常の属性を使用してください。使用 @State 現在のビューに一時的な状態がある場合。サブビューが親ビューの同じ状態を変更する必要がある場合は、次を使用します。 @Binding。
データがモデル層に入ると、 @State 十分ではありません。この講義では、読書アプリを使用して説明します。読書の進行状況、完了ステータス、表紙の読み込み、グローバル ブック リストには異なるライフ サイクルがあります。 SwiftUI はこれらのライフサイクルを次のように分離します。 ObservableObject、 @ObservedObject、 @StateObject、 @EnvironmentObject、 @SceneStorage そして @AppStorageこれにより、インターフェイスは手動でイベントをリッスンするのではなく、データに依存できるようになります。
最後の段落ではパフォーマンスに焦点を当てます。 body インターフェイスのみを記述する必要があり、高価なオブジェクトを作成したり、その中でブロッキング作業を行ったりすることはできません。新しく導入されたのは、 @StateObject 繰り返し作成するというよくある間違いを解決します。 ObservableObject ビューの初期化パスに問題があるため、更新が遅くなり、データがリセットされます。
詳細
@State 現在のビューが所有する一時的な状態を保存します
(05:09)
講義は読書の進捗状況を更新するシートから始まります。 BookView シートが表示されているかどうか、ユーザーのメモ、現在の進行状況を保存する必要があります。これらのフィールドは一緒に変化するため、Apple はそれらを 1 つの値の型に収集します。 EditorConfig そしてそれらをに引き渡します @State 管理用。
struct EditorConfig {
var isEditorPresented = false
var note = ""
var progress: Double = 0
mutating func present(initialProgress: Double) {
progress = initialProgress
note = ""
isEditorPresented = true
}
}
struct BookView: View {
@State private var editorConfig = EditorConfig()
func presentEditor() { editorConfig.present(…) }
var body: some View {
…
Button(action: presentEditor) { … }
…
}
}
キーポイント:
EditorConfig3 つの関連フィールドがまとめられるため、進行状況、メモ、表示ステータスが複数のプロパティに分散されません。present(initialProgress:)ですmutatingメソッド。値自体に対して更新が行われることを示します。@StateSwiftUI にこのストレージを管理するように指示します。ビュー構造体が再作成されると、SwiftUI は既存の状態に再接続します。presentEditor()リクエストのみEditorConfig状態を更新し、状態の変化に応じて SwiftUI によってインターフェイスの更新が完了します。
@Binding 変更のために同じ状態をサブビューに渡します
(07:22)
ProgressEditor を変更する必要があります EditorConfig の BookView。値を直接渡すと、サブビューはコピーにのみ変更されます。サブビューが独自の宣言を行っている場合 @State、2番目の真実の情報源が生成されます。の機能 @Binding 親ビューステートの読み取りおよび書き込みエントリを渡すことです。
struct EditorConfig {
var isEditorPresented = false
var note = ""
var progress: Double = 0
}
struct BookView: View {
@State private var editorConfig = EditorConfig()
var body: some View {
…
ProgressEditor(editorConfig: $editorConfig)
…
}
}
struct ProgressEditor: View {
@Binding var editorConfig: EditorConfig
…
TextEditor($editorConfig.note)
…
}
キーポイント:
$editorConfigからの予測値@State、タイプはBinding<EditorConfig>。- 後
ProgressEditor受け取ります@Binding、それは所有する同じ状態を変更しますBookView。 TextEditorそれ自体もバインディングを受けます。$editorConfig.note既存のバインディングから派生した文字列バインディングです。- バインディングは特定のソースから独立しています。それはから来る可能性があります
@State、または基礎となるモデル オブジェクトまたはストレージ ラッパーから。
ObservableObject モデルの変更をビューに公開します
(13:15)
この講演では、データの永続化、同期化、または副作用の処理が必要な場合にモデル層に入ることが推奨されています。 ObservableObject は、オブジェクトが変更されようとしていることを SwiftUI に通知するクラス制約プロトコルです。 objectWillChange 出版社。一般的な書き方は次のようになります @Published 変更されるプロパティについて。
/// The current reading progress for a specific book.
class CurrentlyReading: ObservableObject {
let book: Book
@Published var progress: ReadingProgress
// …
}
struct ReadingProgress {
struct Entry : Identifiable {
let id: UUID
let progress: Double
let time: Date
let note: String?
}
var entries: [Entry]
}
キーポイント:
CurrentlyReading特定の書籍の読書状況を管理するために使用される参照型です。- 使用
letのためにbookなぜなら、本自体はスピーチ中に変更されないからです。 progress用途@Published, そのため、レコードの変更を読み取る前に、SwiftUI の依存関係の更新がトリガーされます。ReadingProgressは依然として値型です。この講演では、値型を使用してデータをモデル化し、参照型を使用してライフサイクルと副作用を管理できることが明確になりました。
@ObservedObject 外部モデルを指します。 @StateObject モデルのライフサイクルを所有する
(15:36)
@ObservedObject 読書に適しています ObservableObject 外から入ってきた。これにより、SwiftUI がオブジェクトのサブスクライブを行います。 objectWillChangeただし、このインスタンスを作成して維持する責任はありません。
struct BookView: View {
@ObservedObject var currentlyReading: CurrentlyReading
var body: some View {
VStack {
BookCard(
currentlyReading: currentlyReading)
//…
ProgressDetailsList(
progress: currentlyReading.progress)
}
}
}
キーポイント:
currentlyReadingがこのビューの信頼できる情報源ですが、インスタンスのライフサイクルは外部で管理されます。BookCardそしてProgressDetailsListどちらも同じオブジェクトからデータを読み取り、変更されたときに一緒に更新します。- ビューは、データがインターフェイスにどのようにマップされるかを宣言するだけであり、手書きのサブスクリプション コードや更新コードは必要ありません。
(20:20)
@StateObject は 2020 年の新機能です。ビューが ObservableObject を持つのに適しています。この講義では例としてカバー ローダーを使用します。カバーはビューが表示されている場合にのみ必要であり、ローダーはビューのライフ サイクルに従って作成およびリリースされる必要があります。
struct BookCoverView: View {
@StateObject var loader = CoverImageLoader()
var coverName: String
var size: CGFloat
var body: some View {
CoverImage(loader.image, size: size)
.onAppear { loader.load(coverName) }
}
}
キーポイント:
@StateObject実行前に SwiftUI がインスタンス化する初期値を提供しますbody初めて。- SwiftUI は保持されます
loaderビューのライフサイクル全体を通じて、ビュー構造体が再構築されるときにオブジェクトが失われないようにします。 onAppear読み込みアクションのみをトリガーします。オブジェクトが解放されると、CoverImageLoaderそれ自体で作業をキャンセルできますdeinit。
(26:39)
スピーチでは、特に間違った書き方を比較しました。 ReadingListStore() 直接 @ObservedObject 財産。ビュー構造体には安定したライフサイクルがないため、次の場合に繰り返しオブジェクトが割り当てられます。 body 再構築されるため、更新が遅くなり、データが失われます。修正方法は使用することです @StateObject その代わり。
struct ReadingListViewer: View {
var body: some View {
NavigationView {
ReadingList()
Placeholder()
}
}
}
struct ReadingList: View {
@StateObject var store = ReadingListStore()
var body: some View {
// ...
}
}
キーポイント:
ReadingListStoreが所有する真実の情報源ですReadingList。@StateObjectSwiftUI が適切なタイミングでオブジェクトを作成し、ビューの存続期間中それを永続化できるようにします。- この修正により、ヒープ割り当ての繰り返しとオブジェクトのリセットという 2 つの問題が同時に解決されます。
@SceneStorage そして @AppStorage 軽量 UI 状態を保存する
(32:43)
のライフサイクル @State そして @StateObject というプロセスに関係しています。アプリがシステムによって終了された後は、自動的には復元されません。スピーチで紹介されるのは、 Storage ファミリ。軽量状態を保存するために使用されます。 @SceneStorage 列インターフェイスで現在選択されているブックなど、各シーンを個別に保存するのに適しています。
struct ReadingListViewer: View {
@SceneStorage("selection") var selection: String?
var body: some View {
NavigationView {
ReadingList(selection: $selection)
BookDetailPlaceholder()
}
}
}
キーポイント:
"selection"ストレージキーです。同じ種類のデータには、安定した一意のキーが必要です。selectionのように使用できます@State、バインディングは次の方法でも生成できます。$selection。- 各シーンは、iPad または Mac 上の複数のウィンドウに適した独自の選択状態を持つことができます。
(33:49)
@AppStorage 用途 UserDefaults 小さなアプリレベルのデータを保存します。音声の例は、表紙画像を更新するかどうかと読書の進行状況を同期するかどうかの 2 つの設定スイッチです。
struct BookClubSettings: View {
@AppStorage("updateArtwork") private var updateArtwork = true
@AppStorage("syncProgress") private var syncProgress = true
var body: some View {
Form {
Toggle(isOn: $updateArtwork) {
//...
}
Toggle(isOn: $syncProgress) {
//...
}
}
}
}
キーポイント:
- 各設定は 2 つの独立したデータであるため、独自のキーを使用します。
@AppStorageデフォルトでは標準ユーザーのデフォルトを使用します。- バインディングを通じて設定値を変更すると、SwiftUI が自動的に保存および復元します。
- このスピーチでは、永続化にはコストがかかるため、すべてのデータをストレージ ラッパーに詰め込まないように注意を促しています。
重要ポイント
-
読書進捗エディタ: 機能: 読書、コース、またはフィットネス アプリのメモを含む進捗更新シートを作成します。 実行する価値がある理由:
EditorConfigこのセッションでは、複数の関連フィールドを 1 つに収集する方法を説明します。@State価値。 開始方法: まず値型構成オブジェクトを定義し、次に値型構成オブジェクトを渡します$editorConfig編集サブビューに移動します。 -
カバーまたはアバターの読み込みビュー: 何をすべきか: ネットワーク イメージ ローダーを独立した SwiftUI ビューにカプセル化します。 実行する価値がある理由:
@StateObjectこれにより、ビューのライフサイクルに従ってローダーが存続し、割り当てと状態のリセットの繰り返しが減少します。 開始方法: ローダーを準拠させるObservableObject、で作成します@StateObjectビュー内で読み込みをトリガーしますonAppear。 -
マルチウィンドウの選択状態の復元: 何をするか: iPad または Mac のリスト詳細インターフェイスで各ウィンドウの現在の選択を復元します。 実行する価値がある理由:
@SceneStorageはシーンごとのストレージであり、軽量の UI 状態の保存に適しています。 開始方法: 選択値の一意のキーを定義し、渡す$selectionリストビューへ。 -
軽量設定ページ: 何をするか: 同期するかどうか、表紙を更新するかどうか、特定の領域を表示するかどうかなどのスイッチを実装します。 実行する価値がある理由:
@AppStorageに直接接続しますUserDefaults、小規模なアプリレベルの設定に適しています。 開始方法:@AppStorage各設定のプロパティを設定し、バインディングを渡すToggle。 -
グローバル データ入力: 何をすべきか: 書籍リスト、アカウント ステータス、または現在のワークスペース モデルを SwiftUI アプリのルートに置きます。 実行する価値がある理由: スピーチは以下を使用してデモンストレーションされました
@StateObjectで@main Appすべてのシーンがモデルの変更に対応できるように、アプリ全体にわたる信頼できる情報源を作成します。 開始方法: 宣言する@StateObject private var storeアプリ タイプでストアをルート ビューまたはインジェクション環境に渡します。
関連セッション
- SwiftUI のアプリの要点 — SwiftUI アプリ、シーン、WindowGroup、およびアプリレベルの信頼できる情報源について引き続き話します。
- SwiftUI の新機能 — アプリ/シーン API、コントロール、グリッド、ウィジェットのサポートを含む、2020 年の SwiftUI アップデートの概要。
- SwiftUI でドキュメントベースのアプリを構築する — 方法を示します
DocumentGroupドキュメントデータ、シーン、SwiftUIをバインドします。 - SwiftUI のスタック、グリッド、アウトライン — SwiftUI での詳細データと階層データの表示方法に注目してください。
コメント
GitHub Issues · utterances