swiftui-ui-patterns

swiftui-ui-patterns

热门

构建 SwiftUI 视图和组件的最佳实践与示例驱动指南,涵盖导航层级、自定义视图修饰符以及使用堆栈和网格的响应式布局。适用于创建或重构 SwiftUI 界面、使用 TabView 设计标签架构、使用 VStack/HStack 组合屏幕、管理 @State 或 @Binding、构建声明式 iOS 界面,或需要特定组件的模式和示例。

3854Star
203Fork
更新于 2026/3/29
SKILL.md
readonly只读
name
swiftui-ui-patterns
description

构建 SwiftUI 视图和组件的最佳实践与示例驱动指南,涵盖导航层级、自定义视图修饰符以及使用堆栈和网格的响应式布局。适用于创建或重构 SwiftUI 界面、使用 TabView 设计标签架构、使用 VStack/HStack 组合屏幕、管理 @State 或 @Binding、构建声明式 iOS 界面,或需要特定组件的模式和示例。

SwiftUI UI 模式

快速开始

根据目标选择路线:

现有项目

  • 确定功能或屏幕及其主要交互模型(列表、详情、编辑器、设置、标签页)。
  • 在仓库中查找附近示例,使用 rg "TabView\(" 或类似命令,然后阅读最近的 SwiftUI 视图。
  • 应用本地约定:优先使用 SwiftUI 原生状态,尽可能保持状态本地化,并使用环境注入共享依赖。
  • references/components-index.md 中选择相关组件参考并遵循其指导。
  • 如果交互通过拖拽或滚动主内容来显示次要内容,请先阅读 references/scroll-reveal.md,再手动实现手势。
  • 使用小型、专注的子视图和 SwiftUI 原生数据流构建视图。

新项目脚手架

  • references/app-wiring.md 开始,连接 TabView + NavigationStack + sheets。
  • 基于提供的骨架添加最小化的 AppTabRouterPath
  • 根据你首先需要的 UI 选择下一个组件参考(TabView、NavigationStack、Sheets)。
  • 随着新屏幕的添加,扩展路由和 sheet 枚举。

通用规则

  • 使用现代 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,并在模式依赖特定 OS 时注明最低 OS 版本。
  • 仅在编辑遗留文件时保持现有遗留模式。
  • 遵循项目的格式化器和风格指南。
  • Sheets:当状态表示选中的模型时,优先使用 .sheet(item:) 而不是 .sheet(isPresented:)。避免在 sheet 内部使用 if let。Sheets 应拥有自己的操作,并在内部调用 dismiss(),而不是转发 onCancel/onConfirm 闭包。
  • 滚动驱动揭示:优先从滚动偏移量推导出归一化的进度值,并以此单一数据源驱动视觉状态。除非仅靠滚动无法表达交互,否则避免使用并行手势状态机。

状态所有权总结

使用与所有权模型匹配的最窄状态工具:

场景 首选模式
单个视图拥有的本地 UI 状态 @State
子视图修改父视图拥有的值类型状态 @Binding
iOS 17+ 上根拥有的引用类型模型 @State 配合 @Observable 类型
iOS 17+ 上子视图读取或修改注入的 @Observable 模型 将其作为存储属性显式传递
共享应用服务或配置 @Environment(Type.self)
iOS 16 及更早版本上的遗留引用类型模型 根使用 @StateObject,注入时使用 @ObservedObject

先选择所有权位置,再选择包装器。当普通值类型状态足够时,不要引入引用类型模型。

横切参考

  • 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:稳定标识、观察范围、惰性容器和渲染成本护栏。

反模式

  • 巨型视图,将布局、业务逻辑、网络、路由和格式化混合在一个文件中。
  • 使用多个布尔标志表示互斥的 sheets、警报或导航目标。
  • 直接在 body 驱动的代码路径中调用实时服务,而不是使用视图生命周期钩子或注入的模型/服务。
  • 使用 AnyView 解决类型不匹配问题,而应通过更好的组合来解决。
  • 在没有明确所有权原因的情况下,将每个共享依赖默认设置为 @EnvironmentObject 或全局路由器。

新 SwiftUI 视图的工作流程

  1. 在编写 UI 代码之前,定义视图的状态、所有权位置和最低 OS 假设。
  2. 确定哪些依赖属于 @Environment,哪些应作为显式的初始化器输入。
  3. 草拟视图层次结构、路由模型和展示点;将重复部分提取到子视图中。对于复杂导航,请阅读 references/navigationstack.mdreferences/sheets.mdreferences/deeplinks.md构建并确认无编译器错误后再继续。
  4. 使用 .task.task(id:) 实现异步加载,并在需要时添加显式的加载和错误状态。当工作依赖于变化的输入或取消时,阅读 references/async-state.md
  5. 为主要和次要状态添加预览,然后在 UI 可交互时添加无障碍标签或标识符。当视图需要测试夹具或注入的模拟依赖时,阅读 references/previews.md
  6. 通过构建验证:确认无编译器错误,检查预览是否正常渲染,确保状态变化正确传播,并检查列表标识和观察范围不会导致可避免的重新渲染。如果屏幕很大、滚动很多或频繁更新,请阅读 references/performance.md。对于常见的 SwiftUI 编译错误——缺少 @State 注解、ViewBuilder 闭包歧义或泛型类型不匹配——在更新调用点之前解决它们。如果构建失败: 仔细阅读错误消息,修复识别的问题,然后重新构建,再继续下一步。如果预览崩溃,隔离有问题的子视图,确认其状态初始化有效,然后重新运行预览再继续。

组件参考

使用 references/components-index.md 作为入口点。每个组件参考应包括:

  • 意图和最佳适用场景。
  • 符合本地约定的最小使用模式。
  • 陷阱和性能说明。
  • 当前仓库中现有示例的路径。

添加新的组件参考

  • 创建 references/<component>.md
  • 保持简短且可操作;链接到当前仓库中的具体文件。
  • 使用新条目更新 references/components-index.md