ハイライト
SwiftUI のレンダリング動作は、3 つの中心的な概念によって決定されます。ID はビューが再利用されるかどうかを決定し、Lifetime は状態がいつリセットされるかを決定し、Dependency はいつ更新されるかを決定します。これら 3 つを理解すれば、SwiftUI の奇妙なバグのほとんどを取り除くことができます。
主な内容
SwiftUI を作成するとき、開発者はよく 3 種類の問題に遭遇します。ステータスが不可解にリセットされる、アニメーションがトリガーされない、ビューが頻繁に更新されすぎるということです。これらの問題は、表面的にはバグのように見えますが、根本的な原因は、多くの場合、SwiftUI のコアメカニズムの理解が不十分であることです。
このセッションでは新しい API については説明しませんが、SwiftUI の根底にある哲学についての詳細な分析を提供します。
アイデンティティ: SwiftUI がビューを識別する方法
Identity は、「このビューとあのビューは 2 つのレンダリングで同じですか?」という質問に答えます。
SwiftUI は 2 つの ID を使用します。
構造的同一性: ビュー ツリー内のビューの位置によって決定されます。同じ位置は同じビューを意味します。
明示的なアイデンティティ: パス.id()修飾子が指定されました。ID が変更されると、SwiftUI はそれが完全に新しいビューであると認識します。
ForEach(items) { item in
RowView(item: item)
}
ここitemIDはそれぞれですRowView身元。あなたが置くならitems並べ替える場合、SwiftUI は既存のビュー インスタンスを再利用し、データのみを更新します。
しかし、次のように書かれているとします。
ForEach(0..<items.count, id: \.self) { index in
RowView(item: items[index])
}
要素が配列の中央に挿入されると、後続のすべての要素のインデックスと ID が変更されます。 SwiftUI はこれらのビューを破棄して再構築し、すべての状態が失われます。
ライフタイム: 状態のライフサイクル
@Stateそして@StateObjectライフサイクルはビューの ID にバインドされています。アイデンティティが変わらなければ、状態は保持されます。 ID が変更されると、状態はリセットされます。
struct CounterView: View {
@State private var count = 0
var body: some View {
Button("Count: \(count)") {
count += 1
}
.id(someValue) // 当 someValue 变化,count 重置为 0
}
}
依存関係: SwiftUI がいつ更新するかを知る方法
SwiftUI のbodyすべての読み取り依存関係は、実行中に自動的に追跡されます。依存関係が変更されると、ビューが自動的に更新されます。
struct UserView: View {
@ObservedObject var user: User
var body: some View {
Text(user.name) // SwiftUI 记住了:这个视图依赖 user.name
}
}
もしuser.name変更すると、SwiftUI がこのビューを更新します。しかし、もしuser.ageこのビューでは読み込まずに変更されますuser.age, SwiftUIでは更新されません。
##詳細
よくあるアイデンティティの罠
条件分岐切り替えビュータイプ
if isEditing {
TextField("Name", text: $name)
} else {
Text(name)
}
いつisEditing切り替えると、TextField と Text はビュー ツリー内の同じ位置を占めますが、タイプは異なります。SwiftUI は TextField を破棄して Text を作成し、次に切り替えるときに Text を破棄して新しい TextField を作成します。TextField のフォーカス状態が失われます。
修正: 安定した ID を両方のビューに追加します。
if isEditing {
TextField("Name", text: $name)
.id("nameField")
} else {
Text(name)
.id("nameField")
}
このようにして、SwiftUI は、タイプが異なるだけで、それらが同じ論理ビューであることを認識します。
ForEach は不安定な ID を使用します
ForEach(items.indices, id: \.self) { index in
// index 作为 id,插入元素后全部重建
}
修正: データ自体の安定した識別を使用します。
ForEach(items, id: \.id) { item in
// item.id 是稳定的
}
@StateObject vs @ObservedObject
struct ParentView: View {
@StateObject private var model = ViewModel()
var body: some View {
ChildView(model: model)
}
}
struct ChildView: View {
@ObservedObject var model: ViewModel
var body: some View {
Text(model.title)
}
}
キーポイント:
@StateObjectビューが最初に作成されたときに初期化し、後で同じインスタンスを再利用します。@ObservedObject外部から渡されたオブジェクトを観察するだけで、そのライフサイクルを管理しない- 決して入ってはいけません
body作成年ObservableObjectインスタンスは、更新するたびに新しいインスタンスが作成されます
依存関係の正確な追跡
SwiftUI の依存関係の追跡はプロパティ レベルで行われます。
struct DetailView: View {
let item: Item
var body: some View {
VStack {
Text(item.title)
Text(item.description)
}
}
}
もしitem親ビューが新しい値を渡すたびに、値型 (構造体) になります。item(内容が同じでも)SwiftUIが更新されますDetailView。
もしitem参照型(クラス)であり、SwiftUIは参照アドレスを比較します。別のインスタンスが渡された場合にのみ更新されます。
重要ポイント
1.安定したアイデンティティを使用してリストを設計します
編集可能なリストを実装する場合は、各セルの ID が配列インデックスではなくデータのビジネス ID に基づいていることを確認してください。このようにして、SwiftUI は挿入、削除、並べ替えの際にビューを正しく再利用できるようになり、アニメーションがよりスムーズになります。入口API:ForEach(items, id: \.id)。
2. ViewModel のライフサイクルをビューから切り離します
使用@StateObjectルート ビューで ViewModel を作成します。@ObservedObjectまたはサブビューに渡される環境オブジェクト。このようにして、ID の変更によりサブビューが再構築された場合でも、データは失われません。入口API:@StateObject + environmentObject()。
3. EquatableView を使用して大量の静的コンテンツを最適化します
めったに変更されない多数のサブビューを含むコンテナの場合は、次のように実装します。Equatableプロトコルとしてパッケージ化されていますEquatableView, SwiftUI が不要な diff をスキップできるようにします。入口API:static func == + .equatable()。
4.状態リセットの問題の診断
State が予期せずリセットされたことに気付いた場合は、次の 3 つの点を確認してください。.id()そして、ForEach の ID が安定しているかどうか、条件分岐内のビューが同じ位置を共有しているかどうかなど、値が変更されました。Xcode の SwiftUI デバッグ ツールを使用して、ビュー ツリーで ID の変更を表示します。
5.予測可能なリフレッシュ境界を設計する
ページ全体の更新がトリガーされるのを避けるために、頻繁に変更されるデータ (タイマー、センサー データなど) を独立したサブビューに分離します。エントランスAPI:動的部分を独立したViewに抽出し、必要なデータのみを渡します。
関連セッション
- SwiftUI アプリにリッチなグラフィックを追加 — Canvas と TimelineView を使用したグラフィック描画
- SwiftUI でフォーカスを直接および反映 — フォーカス管理とビューの対話
- Mac 上の SwiftUI: 基礎を構築する — macOS での SwiftUI アーキテクチャの実践
- SwiftUI での同時実行性の発見 — 非同期データ フローとビューの更新
コメント
GitHub Issues · utterances