swiftui-ui-patterns

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SwiftUI UI Patterns

SwiftUI 界面模式

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 + 弹窗(sheets)。
  • 基于提供的骨架添加最小化的
    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
    中添加新条目。