SwiftUI UI Patterns
Quick start
Choose a track based on your goal:
Existing project
- - Identify the feature or screen and the primary interaction model (list, detail, editor, settings, tabbed).
- Find a nearby example in the repo with
rg "TabView\(" or similar, then read the closest SwiftUI view. - Apply local conventions: prefer SwiftUI-native state, keep state local when possible, and use environment injection for shared dependencies.
- Choose the relevant component reference from
references/components-index.md and follow its guidance. - Build the view with small, focused subviews and SwiftUI-native data flow.
New project scaffolding
- - Start with
references/app-scaffolding-wiring.md to wire TabView + NavigationStack + sheets. - Add a minimal
AppTab and RouterPath based on the provided skeletons. - Choose the next component reference based on the UI you need first (TabView, NavigationStack, Sheets).
- Expand the route and sheet enums as new screens are added.
General rules to follow
- - Use modern SwiftUI state (
@State, @Binding, @Observable, @Environment) and avoid unnecessary view models. - Prefer composition; keep views small and focused.
- Use async/await with
.task and explicit loading/error states. - Maintain existing legacy patterns only when editing legacy files.
- Follow the project's formatter and style guide.
- Sheets: Prefer
.sheet(item:) over .sheet(isPresented:) when state represents a selected model. Avoid if let inside a sheet body. Sheets should own their actions and call dismiss() internally instead of forwarding onCancel/onConfirm closures.
Workflow for a new SwiftUI view
- 1. Define the view's state and its ownership location.
- Identify dependencies to inject via
@Environment. - Sketch the view hierarchy and extract repeated parts into subviews.
- Implement async loading with
.task and explicit state enum if needed. - Add accessibility labels or identifiers when the UI is interactive.
- Validate with a build and update usage callsites if needed.
Component references
Use references/components-index.md as the entry point. Each component reference should include:
- - Intent and best-fit scenarios.
- Minimal usage pattern with local conventions.
- Pitfalls and performance notes.
- Paths to existing examples in the current repo.
Sheet patterns
Item-driven sheet (preferred)
CODEBLOCK0
Sheet owns its actions
CODEBLOCK1
Adding a new component reference
- - Create
references/<component>.md. - Keep it short and actionable; link to concrete files in the current repo.
- Update
references/components-index.md with the new entry.
SwiftUI UI 模式
快速开始
根据你的目标选择一条路径:
现有项目
- - 确定功能或界面以及主要的交互模型(列表、详情、编辑器、设置、标签页)。
- 在仓库中使用 rg TabView\( 或类似命令查找附近的示例,然后阅读最接近的 SwiftUI 视图。
- 应用本地约定:优先使用 SwiftUI 原生状态,尽可能保持状态本地化,并使用环境注入处理共享依赖。
- 从 references/components-index.md 中选择相关的组件参考文档,并遵循其指导。
- 使用小型、专注的子视图和 SwiftUI 原生数据流构建视图。
新项目脚手架
- - 从 references/app-scaffolding-wiring.md 开始,连接 TabView + NavigationStack + sheets。
- 基于提供的骨架添加最小的 AppTab 和 RouterPath。
- 根据你首先需要的 UI 选择下一个组件参考文档(TabView、NavigationStack、Sheets)。
- 随着新界面的添加,扩展路由和 sheet 枚举。
通用规则
- - 使用现代 SwiftUI 状态(@State、@Binding、@Observable、@Environment),避免不必要的视图模型。
- 优先使用组合模式;保持视图小巧且专注。
- 使用带有 .task 的 async/await 和显式的加载/错误状态。
- 仅在编辑遗留文件时保持现有的遗留模式。
- 遵循项目的格式化工具和风格指南。
- Sheets:当状态代表选中的模型时,优先使用 .sheet(item:) 而非 .sheet(isPresented:)。避免在 sheet 主体内使用 if let。Sheets 应拥有自己的操作并在内部调用 dismiss(),而不是转发 onCancel/onConfirm 闭包。
新 SwiftUI 视图的工作流程
- 1. 定义视图的状态及其所有权位置。
- 确定需要通过 @Environment 注入的依赖。
- 勾勒视图层次结构,将重复部分提取到子视图中。
- 使用 .task 实现异步加载,必要时使用显式的状态枚举。
- 当 UI 可交互时,添加无障碍标签或标识符。
- 通过构建验证,并在需要时更新使用调用点。
组件参考文档
使用 references/components-index.md 作为入口点。每个组件参考文档应包含:
- - 意图和最佳适用场景。
- 带有本地约定的最小使用模式。
- 陷阱和性能注意事项。
- 当前仓库中现有示例的路径。
Sheet 模式
基于项目的 Sheet(推荐)
swift
@State private var selectedItem: Item?
.sheet(item: $selectedItem) { item in
EditItemSheet(item: item)
}
Sheet 拥有自己的操作
swift
struct EditItemSheet: View {
@Environment(\.dismiss) private var dismiss
@Environment(Store.self) private var store
let item: Item
@State private var isSaving = false
var body: some View {
VStack {
Button(isSaving ? 保存中… : 保存) {
Task { await save() }
}
}
}
private func save() async {
isSaving = true
await store.save(item)
dismiss()
}
}
添加新的组件参考文档
- - 创建 references/.md。
- 保持简短且可操作;链接到当前仓库中的具体文件。
- 使用新条目更新 references/components-index.md。