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.
  • Build the view with small, focused subviews and SwiftUI-native data flow.
  • 确定功能或页面,以及核心交互模式(列表、详情、编辑器、设置、标签页)。
  • 使用
    rg "TabView\("
    或类似命令在仓库中查找相关示例,然后阅读最匹配的SwiftUI视图代码。
  • 遵循本地约定:优先使用SwiftUI原生状态,尽可能保持状态局部化,对共享依赖使用环境注入。
  • references/components-index.md
    中选择相关的组件参考并遵循其指引。
  • 使用小型、职责单一的子视图和SwiftUI原生数据流构建视图。

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.
  • references/app-scaffolding-wiring.md
    开始,搭建TabView + NavigationStack + 弹窗的基础结构。
  • 基于提供的骨架代码添加最简的
    AppTab
    RouterPath
  • 根据你首先需要的UI(TabView、NavigationStack、Sheets)选择对应的组件参考。
  • 随着新页面的添加,扩展路由和弹窗枚举。

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.
  • 使用现代SwiftUI状态(
    @State
    @Binding
    @Observable
    @Environment
    ),避免不必要的视图模型。
  • 优先使用组合模式;保持视图小型且职责单一。
  • 将async/await与
    .task
    结合使用,并明确设置加载/错误状态。
  • 仅在编辑遗留文件时保留现有遗留模式。
  • 遵循项目的格式化工具和风格指南。
  • 弹窗(Sheets):当状态对应选中的模型时,优先使用
    .sheet(item:)
    而非
    .sheet(isPresented:)
    。避免在弹窗主体内部使用
    if let
    。弹窗应自行管理其操作,内部调用
    dismiss()
    ,而非传递
    onCancel
    /
    onConfirm
    闭包。

Workflow for a new SwiftUI view

新建SwiftUI视图的工作流

  1. Define the view's state and its ownership location.
  2. Identify dependencies to inject via
    @Environment
    .
  3. Sketch the view hierarchy and extract repeated parts into subviews.
  4. Implement async loading with
    .task
    and explicit state enum if needed.
  5. Add accessibility labels or identifiers when the UI is interactive.
  6. Validate with a build and update usage callsites if needed.
  1. 定义视图的状态及其归属位置。
  2. 确定需要通过
    @Environment
    注入的依赖。
  3. 梳理视图层级,将重复部分提取为子视图。
  4. 如有需要,使用
    .task
    实现异步加载和明确的状态枚举。
  5. 当UI具备交互性时,添加无障碍标签或标识符。
  6. 构建验证,如有需要更新调用处代码。

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
作为入口。每个组件参考应包含:
  • 用途和最佳适用场景。
  • 符合本地约定的最简使用模式。
  • 常见陷阱和性能注意事项。
  • 指向当前仓库中现有示例的路径。

Sheet patterns

弹窗模式

Item-driven sheet (preferred)

数据驱动弹窗(推荐)

swift
@State private var selectedItem: Item?

.sheet(item: $selectedItem) { item in
    EditItemSheet(item: item)
}
swift
@State private var selectedItem: Item?

.sheet(item: $selectedItem) { item in
    EditItemSheet(item: item)
}

Sheet owns its actions

弹窗自行管理操作

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 ? "Saving…" : "Save") {
                Task { await save() }
            }
        }
    }

    private func save() async {
        isSaving = true
        await store.save(item)
        dismiss()
    }
}
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 ? "Saving…" : "Save") {
                Task { await save() }
            }
        }
    }

    private func save() async {
        isSaving = true
        await store.save(item)
        dismiss()
    }
}

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
    ,添加新条目。