
关于
SwiftUI 架构模式、@Observable 状态管理、视图组合、导航、性能优化和现代 iOS/macOS UI 最佳实践。
name: swiftui-patterns description: SwiftUI 架构模式、使用 @Observable 的状态管理、视图组合、导航、性能优化以及现代 iOS/macOS UI 最佳实践。
SwiftUI 模式
用于在 Apple 平台上构建声明式、高性能用户界面的现代 SwiftUI 模式。涵盖 Observation 框架、视图组合、类型安全导航和性能优化。
何时激活
- 构建 SwiftUI 视图和管理状态(
@State、@Observable、@Binding) - 使用
NavigationStack设计导航流程 - 构建视图模型和数据流
- 优化列表和复杂布局的渲染性能
- 在 SwiftUI 中使用环境值和依赖注入
状态管理
属性包装器选择
选择最简单的适合的包装器:
| 包装器 | 用例 |
|---------|----------|
| @State | 视图本地值类型(开关、表单字段、Sheet 展示) |
| @Binding | 对父级 @State 的双向引用 |
| @Observable class + @State | 拥有多个属性的自有模型 |
| @Observable class(无包装器) | 从父级传递的只读引用 |
| @Bindable | 对 @Observable 属性的双向绑定 |
| @Environment | 通过 .environment() 注入的共享依赖 |
@Observable 视图模型
使用 @Observable(而非 ObservableObject)— 它跟踪属性级别的变化,因此 SwiftUI 只重新渲染读取了已更改属性的视图:
@Observable
final class ItemListViewModel {
private(set) var items: [Item] = []
private(set) var isLoading = false
var searchText = ""
private let repository: any ItemRepository
init(repository: any ItemRepository = DefaultItemRepository()) {
self.repository = repository
}
func load() async {
isLoading = true
defer { isLoading = false }
items = (try? await repository.fetchAll()) ?? []
}
}
视图消费视图模型
struct ItemListView: View {
@State private var viewModel: ItemListViewModel
init(viewModel: ItemListViewModel = ItemListViewModel()) {
_viewModel = State(initialValue: viewModel)
}
var body: some View {
List(viewModel.items) { item in
ItemRow(item: item)
}
.searchable(text: $viewModel.searchText)
.overlay { if viewModel.isLoading { ProgressView() } }
.task { await viewModel.load() }
}
}
环境注入
用 @Environment 替代 @EnvironmentObject:
// 注入
ContentView()
.environment(authManager)
// 消费
struct ProfileView: View {
@Environment(AuthManager.self) private var auth
var body: some View {
Text(auth.currentUser?.name ?? "Guest")
}
}
视图组合
提取子视图以限制失效范围
将视图拆分为小型、专注的结构体。当状态变化时,只有读取该状态的子视图会重新渲染:
struct OrderView: View {
@State private var viewModel = OrderViewModel()
var body: some View {
VStack {
OrderHeader(title: viewModel.title)
OrderItemList(items: viewModel.items)
OrderTotal(total: viewModel.total)
}
}
}
使用 ViewModifier 实现可复用样式
struct CardModifier: ViewModifier {
func body(content: Content) -> some View {
content
.padding()
.background(.regularMaterial)
.clipShape(RoundedRectangle(cornerRadius: 12))
}
}
extension View {
func cardStyle() -> some View {
modifier(CardModifier())
}
}
导航
类型安全的 NavigationStack
使用 NavigationStack 配合 NavigationPath 实现编程式、类型安全的路由:
@Observable
final class Router {
var path = NavigationPath()
func navigate(to destination: Destination) {
path.append(destination)
}
func popToRoot() {
path = NavigationPath()
}
}
enum Destination: Hashable {
case detail(Item.ID)
case settings
case profile(User.ID)
}
struct RootView: View {
@State private var router = Router()
var body: some View {
NavigationStack(path: $router.path) {
HomeView()
.navigationDestination(for: Destination.self) { dest in
switch dest {
case .detail(let id): ItemDetailView(itemID: id)
case .settings: SettingsView()
case .profile(let id): ProfileView(userID: id)
}
}
}
.environment(router)
}
}
性能
对大型集合使用懒加载容器
LazyVStack 和 LazyHStack 仅在可见时创建视图:
ScrollView {
LazyVStack(spacing: 8) {
ForEach(items) { item in
ItemRow(item: item)
}
}
}
稳定标识符
在 ForEach 中始终使用稳定、唯一的 ID — 避免使用数组索引作为标识符,因为这会导致不必要的视图重建。
兼容工具
Claude CodeCursor
标签
移动端
