71 lines
2.9 KiB
Markdown
71 lines
2.9 KiB
Markdown
|
|
---
|
|||
|
|
name: swiftui-patterns
|
|||
|
|
description: SwiftUI 架構模式,涵蓋使用 @Observable 進行狀態管理、視圖組合、導航、效能優化以及現代 iOS/macOS UI 最佳實踐。
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# SwiftUI 開發模式 (SwiftUI Patterns)
|
|||
|
|
|
|||
|
|
用於 Apple 平台建構聲明式、高性能用戶介面的現代 SwiftUI 模式。內容涵蓋 Observation 框架、視圖組合 (View Composition)、類型安全導航以及效能優化。
|
|||
|
|
|
|||
|
|
## 何時啟用
|
|||
|
|
|
|||
|
|
- 編寫 SwiftUI View 並管理狀態 (`@State`, `@Observable`, `@Binding`)。
|
|||
|
|
- 使用 `NavigationStack` 設計導航流程。
|
|||
|
|
- 規劃 View Model 架構與數據流。
|
|||
|
|
- 優化長列表 (Lists) 或複雜佈局的渲染效能。
|
|||
|
|
- 在 SwiftUI 中使用環境值 (Environment Values) 與依賴注入。
|
|||
|
|
|
|||
|
|
## 狀態管理 (State Management)
|
|||
|
|
|
|||
|
|
### 屬性包裝器 (Property Wrappers) 選用指南
|
|||
|
|
|
|||
|
|
| 包裝器 | 適用場景 |
|
|||
|
|
|---------|----------|
|
|||
|
|
| `@State` | View 本地數據(開關狀態、表單欄位、Sheet 顯示開關)。 |
|
|||
|
|
| `@Binding` | 對父視圖 `@State` 的雙向引用。 |
|
|||
|
|
| `@Observable` | 現代化的數據模型(ViewModel),僅在屬性變動時更新相關視圖。 |
|
|||
|
|
| `@Bindable` | 實現對 `@Observable` 屬性的雙向綁定。 |
|
|||
|
|
| `@Environment` | 跨層級共享的依賴項注入。 |
|
|||
|
|
|
|||
|
|
### 現代化 ViewModel (@Observable)
|
|||
|
|
|
|||
|
|
優先使用 `@Observable` 巨集而非舊有的 `ObservableObject`:
|
|||
|
|
|
|||
|
|
```swift
|
|||
|
|
@Observable
|
|||
|
|
final class ItemListViewModel {
|
|||
|
|
private(set) var items: [Item] = []
|
|||
|
|
private(set) var isLoading = false
|
|||
|
|
var searchText = ""
|
|||
|
|
|
|||
|
|
func load() async {
|
|||
|
|
isLoading = true
|
|||
|
|
defer { isLoading = false }
|
|||
|
|
// 執行非同步載入邏輯
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 視圖組合 (View Composition)
|
|||
|
|
|
|||
|
|
- **精簡化視圖結構**:將大型視圖拆解為多個小型 Struct。當狀態變動時,SwiftUI 僅會重新渲染讀取該狀態的子視圖。
|
|||
|
|
- **自定義 ViewModifier**:將重複的樣式邏輯封裝為修飾器,提升代碼複用率。
|
|||
|
|
|
|||
|
|
## 導航架構 (Navigation)
|
|||
|
|
|
|||
|
|
使用 `NavigationStack` 結合 `NavigationPath` 實現類型安全且可程式化控制的路由系統。建議將路由邏輯收納於一個 `@Observable` 的 Router 類別中。
|
|||
|
|
|
|||
|
|
## 效能優化 (Performance)
|
|||
|
|
|
|||
|
|
- **延遲加載**:對於大型集合,使用 `LazyVStack` 或 `LazyHStack`。
|
|||
|
|
- **穩定標識符**:在 `ForEach` 中務必使用穩定且唯一的 ID,避免使用陣列索引。
|
|||
|
|
- **避免耗時運算**:嚴禁在 `body` 內執行 I/O、網路或大量運算。非同步任務請使用 `.task {}` 修飾器(它會在視圖消失時自動取消任務)。
|
|||
|
|
- **Equatable 順應性**:對於渲染成本極高的視圖,實作 `Equatable` 以跳過不必要的重複渲染。
|
|||
|
|
|
|||
|
|
## 應避免的反模式
|
|||
|
|
|
|||
|
|
- 在新專案中繼續使用 `ObservableObject` / `@Published` 等舊版 API。
|
|||
|
|
- 使用 `AnyView` 類型抹除,應優先考慮 `@ViewBuilder`、`Group` 或 `switch`。
|
|||
|
|
- 在 `body` 或 `init` 內直接發起非同步請求。
|
|||
|
|
- 忽視跨 Actor 傳遞數據時的 `Sendable` 需求。
|