guide-swiftui-ui-patterns
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGuide Skill — This is an expert workflow/pattern guide, not API reference documentation. Originally from Dimillian/Skills by Thomas Ricouard. MIT License.
指南技能 — 这是一份专家级工作流/模式指南,而非API参考文档。 最初来自Thomas Ricouard的Dimillian/Skills,采用MIT许可证。
SwiftUI UI Patterns
SwiftUI UI模式
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 or similar, then read the closest SwiftUI view.
rg "TabView\(" - 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 and follow its guidance.
references/components-index.md - If the interaction reveals secondary content by dragging or scrolling the primary content away, read before implementing gestures manually.
references/scroll-reveal.md - Build the view with small, focused subviews and SwiftUI-native data flow.
- 确定功能或屏幕以及主要交互模型(列表、详情、编辑器、设置、标签页)。
- 使用或类似命令在仓库中找到相近示例,然后阅读最接近的SwiftUI视图。
rg "TabView\(" - 遵循本地约定:优先使用SwiftUI原生状态,尽可能保持状态本地化,通过环境注入共享依赖。
- 从中选择相关组件参考并遵循其指导。
references/components-index.md - 如果交互需要通过拖动或滚动主内容来显示次要内容,请先阅读再手动实现手势。
references/scroll-reveal.md - 用小型、专注的子视图和SwiftUI原生数据流构建视图。
New project scaffolding
新项目脚手架
- Start with to wire TabView + NavigationStack + sheets.
references/app-wiring.md - Add a minimal and
AppTabbased on the provided skeletons.RouterPath - 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.
- 从开始,配置TabView + NavigationStack + sheets。
references/app-wiring.md - 基于提供的框架添加最小化的和
AppTab。RouterPath - 根据你首先需要的UI(TabView、NavigationStack、Sheets)选择下一个组件参考。
- 添加新屏幕时扩展路由和sheet枚举。
General rules to follow
需遵循的通用规则
- Use modern SwiftUI state (,
@State,@Binding,@Observable) and avoid unnecessary view models.@Environment - If the deployment target includes iOS 16 or earlier and cannot use the Observation API introduced in iOS 17, fall back to with
ObservableObjectfor root ownership,@StateObjectfor injected observation, and@ObservedObjectonly for truly shared app-level state.@EnvironmentObject - Prefer composition; keep views small and focused.
- Use async/await with and explicit loading/error states. For restart, cancellation, and debouncing guidance, read
.task.references/async-state.md - Keep shared app services in , but prefer explicit initializer injection for feature-local dependencies and models. For root wiring patterns, read
@Environment.references/app-wiring.md - Prefer the newest SwiftUI API that fits the deployment target and call out the minimum OS whenever a pattern depends on it.
- Maintain existing legacy patterns only when editing legacy files.
- Follow the project's formatter and style guide.
- Sheets: Prefer over
.sheet(item:)when state represents a selected model. Avoid.sheet(isPresented:)inside a sheet body. Sheets should own their actions and callif letinternally instead of forwardingdismiss()/onCancelclosures.onConfirm - Scroll-driven reveals: Prefer deriving a normalized progress value from scroll offset and driving the visual state from that single source of truth. Avoid parallel gesture state machines unless scroll alone cannot express the interaction.
- 使用现代SwiftUI状态(、
@State、@Binding、@Observable),避免不必要的视图模型。@Environment - 如果部署目标包含iOS 16或更早版本,无法使用iOS 17引入的Observation API,则退而使用:根层级用
ObservableObject持有,注入时用@StateObject观察,仅对真正共享的应用级状态使用@ObservedObject。@EnvironmentObject - 优先组合模式;保持视图小型且专注。
- 结合使用async/await,并显式处理加载/错误状态。如需重启、取消和防抖指导,请阅读
.task。references/async-state.md - 将共享应用服务放在中,但对于功能本地的依赖和模型,优先使用显式初始化注入。如需根层级配置模式,请阅读
@Environment。references/app-wiring.md - 优先选择符合部署目标的最新SwiftUI API,当模式依赖特定版本时,标注最低系统版本。
- 仅在编辑遗留文件时保留现有遗留模式。
- 遵循项目的格式化和风格指南。
- Sheets:当状态表示选中的模型时,优先使用而非
.sheet(item:)。避免在sheet主体内使用.sheet(isPresented:)。Sheets应自行处理操作并在内部调用if let,而非转发dismiss()/onCancel闭包。onConfirm - 滚动驱动显示:优先从滚动偏移量推导标准化进度值,并以此单一数据源驱动视觉状态。除非仅靠滚动无法表达交互,否则避免并行手势状态机。
State ownership summary
状态所有权总结
Use the narrowest state tool that matches the ownership model:
| Scenario | Preferred pattern |
|---|---|
| Local UI state owned by one view | |
| Child mutates parent-owned value state | |
| Root-owned reference model on iOS 17+ | |
Child reads or mutates an injected | Pass it explicitly as a stored property |
| Shared app service or configuration | |
| Legacy reference model on iOS 16 and earlier | |
Choose the ownership location first, then pick the wrapper. Do not introduce a reference model when plain value state is enough.
选择最匹配所有权模型的最窄状态工具:
| 场景 | 首选模式 |
|---|---|
| 单个视图拥有的本地UI状态 | |
| 子视图修改父视图拥有的值状态 | |
| iOS 17+上由根层级拥有的引用模型 | 带 |
iOS 17+上子视图读取或修改注入的 | 作为存储属性显式传递 |
| 共享应用服务或配置 | |
| iOS 16及更早版本上的遗留引用模型 | 根层级用 |
先确定所有权位置,再选择包装器。当普通值状态足够时,不要引入引用模型。
Cross-cutting references
跨领域参考
- : navigation ownership, per-tab history, and enum routing.
references/navigationstack.md - : centralized modal presentation and enum-driven sheets.
references/sheets.md - : URL handling and routing external links into app destinations.
references/deeplinks.md - : root dependency graph, environment usage, and app shell wiring.
references/app-wiring.md - :
references/async-state.md,.task, cancellation, debouncing, and async UI state..task(id:) - :
references/previews.md, fixtures, mock environments, and isolated preview setup.#Preview - : stable identity, observation scope, lazy containers, and render-cost guardrails.
references/performance.md
- :导航所有权、每个标签页的历史记录,以及枚举路由。
references/navigationstack.md - :集中式模态展示和枚举驱动的sheets。
references/sheets.md - :URL处理和将外部链接路由到应用目标。
references/deeplinks.md - :根依赖图、环境使用和应用外壳配置。
references/app-wiring.md - :
references/async-state.md、.task、取消、防抖和异步UI状态。.task(id:) - :
references/previews.md、测试数据、模拟环境和独立预览设置。#Preview - :稳定标识、观察范围、懒加载容器和渲染成本防护措施。
references/performance.md
Anti-patterns
反模式
- Giant views that mix layout, business logic, networking, routing, and formatting in one file.
- Multiple boolean flags for mutually exclusive sheets, alerts, or navigation destinations.
- Live service calls directly inside -driven code paths instead of view lifecycle hooks or injected models/services.
body - Reaching for to work around type mismatches that should be solved with better composition.
AnyView - Defaulting every shared dependency to or a global router without a clear ownership reason.
@EnvironmentObject
- 巨型视图:在一个文件中混合布局、业务逻辑、网络请求、路由和格式化。
- 多个布尔标志用于互斥的sheets、警告或导航目标。
- 在驱动的代码路径中直接调用实时服务,而非视图生命周期钩子或注入的模型/服务。
body - 使用来解决类型不匹配问题,而这些问题本应通过更好的组合来解决。
AnyView - 未经明确所有权验证,就将每个共享依赖默认设为或全局路由。
@EnvironmentObject
Workflow for a new SwiftUI view
新SwiftUI视图的工作流
- Define the view's state, ownership location, and minimum OS assumptions before writing UI code.
- Identify which dependencies belong in and which should stay as explicit initializer inputs.
@Environment - Sketch the view hierarchy, routing model, and presentation points; extract repeated parts into subviews. For complex navigation, read ,
references/navigationstack.md, orreferences/sheets.md. Build and verify no compiler errors before proceeding.references/deeplinks.md - Implement async loading with or
.task, plus explicit loading and error states when needed. Read.task(id:)when the work depends on changing inputs or cancellation.references/async-state.md - Add previews for the primary and secondary states, then add accessibility labels or identifiers when the UI is interactive. Read when the view needs fixtures or injected mock dependencies.
references/previews.md - Validate with a build: confirm no compiler errors, check that previews render without crashing, ensure state changes propagate correctly, and sanity-check that list identity and observation scope will not cause avoidable re-renders. Read if the screen is large, scroll-heavy, or frequently updated. For common SwiftUI compilation errors — missing
references/performance.mdannotations, ambiguous@Stateclosures, or mismatched generic types — resolve them before updating callsites. If the build fails: read the error message carefully, fix the identified issue, then rebuild before proceeding to the next step. If a preview crashes, isolate the offending subview, confirm its state initialisation is valid, and re-run the preview before continuing.ViewBuilder
- 在编写UI代码之前,定义视图的状态、所有权位置和最低系统版本假设。
- 确定哪些依赖属于,哪些应作为显式初始化输入。
@Environment - 勾勒视图层级、路由模型和展示点;将重复部分提取为子视图。如需复杂导航,请阅读、
references/navigationstack.md或references/sheets.md。构建并验证无编译器错误后再继续。references/deeplinks.md - 用或
.task实现异步加载,必要时添加显式加载和错误状态。当工作依赖于变化的输入或取消操作时,请阅读.task(id:)。references/async-state.md - 为主状态和次要状态添加预览,当UI具有交互性时添加无障碍标签或标识符。当视图需要测试数据或注入模拟依赖时,请阅读。
references/previews.md - 通过构建验证:确认无编译器错误,检查预览渲染无崩溃,确保状态变化正确传播,合理检查列表标识和观察范围不会导致不必要的重渲染。如果屏幕较大、滚动频繁或更新频繁,请阅读。对于常见的SwiftUI编译错误——缺失
references/performance.md注解、模糊的@State闭包或不匹配的泛型类型——在更新调用点之前解决这些问题。**如果构建失败:**仔细阅读错误信息,修复已识别的问题,然后重新构建再继续下一步。如果预览崩溃,隔离有问题的子视图,确认其状态初始化有效,重新运行预览后再继续。ViewBuilder
Component references
组件参考
Use as the entry point. Each component reference should include:
references/components-index.md- Intent and best-fit scenarios.
- Minimal usage pattern with local conventions.
- Pitfalls and performance notes.
- Paths to existing examples in the current repo.
以作为入口点。每个组件参考应包含:
references/components-index.md- 用途和最适用场景。
- 符合本地约定的最小使用模式。
- 注意事项和性能说明。
- 当前仓库中现有示例的路径。
Adding a new component reference
添加新组件参考
- Create .
references/<component>.md - Keep it short and actionable; link to concrete files in the current repo.
- Update with the new entry.
references/components-index.md
- 创建文件。
references/<component>.md - 保持内容简短且可操作;链接到当前仓库中的具体文件。
- 在中添加新条目。
references/components-index.md