guide-swiftui-ui-patterns

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
Guide 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
    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.
  • If the interaction reveals secondary content by dragging or scrolling the primary content away, read
    references/scroll-reveal.md
    before implementing gestures manually.
  • Build the view with small, focused subviews and SwiftUI-native data flow.
  • 确定功能或屏幕以及主要交互模型(列表、详情、编辑器、设置、标签页)。
  • 使用
    rg "TabView\("
    或类似命令在仓库中找到相近示例,然后阅读最接近的SwiftUI视图。
  • 遵循本地约定:优先使用SwiftUI原生状态,尽可能保持状态本地化,通过环境注入共享依赖。
  • references/components-index.md
    中选择相关组件参考并遵循其指导。
  • 如果交互需要通过拖动或滚动主内容来显示次要内容,请先阅读
    references/scroll-reveal.md
    再手动实现手势。
  • 用小型、专注的子视图和SwiftUI原生数据流构建视图。

New project scaffolding

新项目脚手架

  • Start with
    references/app-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.
  • references/app-wiring.md
    开始,配置TabView + NavigationStack + sheets。
  • 基于提供的框架添加最小化的
    AppTab
    RouterPath
  • 根据你首先需要的UI(TabView、NavigationStack、Sheets)选择下一个组件参考。
  • 添加新屏幕时扩展路由和sheet枚举。

General rules to follow

需遵循的通用规则

  • Use modern SwiftUI state (
    @State
    ,
    @Binding
    ,
    @Observable
    ,
    @Environment
    ) and avoid unnecessary view models.
  • If the deployment target includes iOS 16 or earlier and cannot use the Observation API introduced in iOS 17, fall back to
    ObservableObject
    with
    @StateObject
    for root ownership,
    @ObservedObject
    for injected observation, and
    @EnvironmentObject
    only for truly shared app-level state.
  • Prefer composition; keep views small and focused.
  • Use async/await with
    .task
    and explicit loading/error states. For restart, cancellation, and debouncing guidance, read
    references/async-state.md
    .
  • Keep shared app services in
    @Environment
    , but prefer explicit initializer injection for feature-local dependencies and models. For root wiring patterns, read
    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
    .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.
  • 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
  • 优先组合模式;保持视图小型且专注。
  • 结合
    .task
    使用async/await,并显式处理加载/错误状态。如需重启、取消和防抖指导,请阅读
    references/async-state.md
  • 将共享应用服务放在
    @Environment
    中,但对于功能本地的依赖和模型,优先使用显式初始化注入。如需根层级配置模式,请阅读
    references/app-wiring.md
  • 优先选择符合部署目标的最新SwiftUI API,当模式依赖特定版本时,标注最低系统版本。
  • 仅在编辑遗留文件时保留现有遗留模式。
  • 遵循项目的格式化和风格指南。
  • Sheets:当状态表示选中的模型时,优先使用
    .sheet(item:)
    而非
    .sheet(isPresented:)
    。避免在sheet主体内使用
    if let
    。Sheets应自行处理操作并在内部调用
    dismiss()
    ,而非转发
    onCancel
    /
    onConfirm
    闭包。
  • 滚动驱动显示:优先从滚动偏移量推导标准化进度值,并以此单一数据源驱动视觉状态。除非仅靠滚动无法表达交互,否则避免并行手势状态机。

State ownership summary

状态所有权总结

Use the narrowest state tool that matches the ownership model:
ScenarioPreferred pattern
Local UI state owned by one view
@State
Child mutates parent-owned value state
@Binding
Root-owned reference model on iOS 17+
@State
with an
@Observable
type
Child reads or mutates an injected
@Observable
model on iOS 17+
Pass it explicitly as a stored property
Shared app service or configuration
@Environment(Type.self)
Legacy reference model on iOS 16 and earlier
@StateObject
at the root,
@ObservedObject
when injected
Choose the ownership location first, then pick the wrapper. Do not introduce a reference model when plain value state is enough.
选择最匹配所有权模型的最窄状态工具:
场景首选模式
单个视图拥有的本地UI状态
@State
子视图修改父视图拥有的值状态
@Binding
iOS 17+上由根层级拥有的引用模型
@Observable
类型的
@State
iOS 17+上子视图读取或修改注入的
@Observable
模型
作为存储属性显式传递
共享应用服务或配置
@Environment(Type.self)
iOS 16及更早版本上的遗留引用模型根层级用
@StateObject
,注入时用
@ObservedObject
先确定所有权位置,再选择包装器。当普通值状态足够时,不要引入引用模型。

Cross-cutting references

跨领域参考

  • references/navigationstack.md
    : navigation ownership, per-tab history, and enum routing.
  • references/sheets.md
    : centralized modal presentation and enum-driven sheets.
  • references/deeplinks.md
    : URL handling and routing external links into app destinations.
  • references/app-wiring.md
    : root dependency graph, environment usage, and app shell wiring.
  • references/async-state.md
    :
    .task
    ,
    .task(id:)
    , cancellation, debouncing, and async UI state.
  • references/previews.md
    :
    #Preview
    , fixtures, mock environments, and isolated preview setup.
  • references/performance.md
    : stable identity, observation scope, lazy containers, and render-cost guardrails.
  • references/navigationstack.md
    :导航所有权、每个标签页的历史记录,以及枚举路由。
  • references/sheets.md
    :集中式模态展示和枚举驱动的sheets。
  • references/deeplinks.md
    :URL处理和将外部链接路由到应用目标。
  • references/app-wiring.md
    :根依赖图、环境使用和应用外壳配置。
  • references/async-state.md
    .task
    .task(id:)
    、取消、防抖和异步UI状态。
  • 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
    body
    -driven code paths instead of view lifecycle hooks or injected models/services.
  • Reaching for
    AnyView
    to work around type mismatches that should be solved with better composition.
  • Defaulting every shared dependency to
    @EnvironmentObject
    or a global router without a clear ownership reason.
  • 巨型视图:在一个文件中混合布局、业务逻辑、网络请求、路由和格式化。
  • 多个布尔标志用于互斥的sheets、警告或导航目标。
  • body
    驱动的代码路径中直接调用实时服务,而非视图生命周期钩子或注入的模型/服务。
  • 使用
    AnyView
    来解决类型不匹配问题,而这些问题本应通过更好的组合来解决。
  • 未经明确所有权验证,就将每个共享依赖默认设为
    @EnvironmentObject
    或全局路由。

Workflow for a new SwiftUI view

新SwiftUI视图的工作流

  1. Define the view's state, ownership location, and minimum OS assumptions before writing UI code.
  2. Identify which dependencies belong in
    @Environment
    and which should stay as explicit initializer inputs.
  3. Sketch the view hierarchy, routing model, and presentation points; extract repeated parts into subviews. For complex navigation, read
    references/navigationstack.md
    ,
    references/sheets.md
    , or
    references/deeplinks.md
    . Build and verify no compiler errors before proceeding.
  4. Implement async loading with
    .task
    or
    .task(id:)
    , plus explicit loading and error states when needed. Read
    references/async-state.md
    when the work depends on changing inputs or cancellation.
  5. Add previews for the primary and secondary states, then add accessibility labels or identifiers when the UI is interactive. Read
    references/previews.md
    when the view needs fixtures or injected mock dependencies.
  6. 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
    references/performance.md
    if the screen is large, scroll-heavy, or frequently updated. For common SwiftUI compilation errors — missing
    @State
    annotations, ambiguous
    ViewBuilder
    closures, 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.
  1. 在编写UI代码之前,定义视图的状态、所有权位置和最低系统版本假设。
  2. 确定哪些依赖属于
    @Environment
    ,哪些应作为显式初始化输入。
  3. 勾勒视图层级、路由模型和展示点;将重复部分提取为子视图。如需复杂导航,请阅读
    references/navigationstack.md
    references/sheets.md
    references/deeplinks.md
    构建并验证无编译器错误后再继续。
  4. .task
    .task(id:)
    实现异步加载,必要时添加显式加载和错误状态。当工作依赖于变化的输入或取消操作时,请阅读
    references/async-state.md
  5. 为主状态和次要状态添加预览,当UI具有交互性时添加无障碍标签或标识符。当视图需要测试数据或注入模拟依赖时,请阅读
    references/previews.md
  6. 通过构建验证:确认无编译器错误,检查预览渲染无崩溃,确保状态变化正确传播,合理检查列表标识和观察范围不会导致不必要的重渲染。如果屏幕较大、滚动频繁或更新频繁,请阅读
    references/performance.md
    。对于常见的SwiftUI编译错误——缺失
    @State
    注解、模糊的
    ViewBuilder
    闭包或不匹配的泛型类型——在更新调用点之前解决这些问题。**如果构建失败:**仔细阅读错误信息,修复已识别的问题,然后重新构建再继续下一步。如果预览崩溃,隔离有问题的子视图,确认其状态初始化有效,重新运行预览后再继续。

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.
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
    references/components-index.md
    with the new entry.
  • 创建
    references/<component>.md
    文件。
  • 保持内容简短且可操作;链接到当前仓库中的具体文件。
  • references/components-index.md
    中添加新条目。