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。 - 基于提供的骨架添加最小化的
AppTab和RouterPath。 - 根据你首先需要的 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 视图的工作流程
- 在编写 UI 代码之前,定义视图的状态、所有权位置和最低 OS 假设。
- 确定哪些依赖属于
@Environment,哪些应作为显式的初始化器输入。 - 草拟视图层次结构、路由模型和展示点;将重复部分提取到子视图中。对于复杂导航,请阅读
references/navigationstack.md、references/sheets.md或references/deeplinks.md。构建并确认无编译器错误后再继续。 - 使用
.task或.task(id:)实现异步加载,并在需要时添加显式的加载和错误状态。当工作依赖于变化的输入或取消时,阅读references/async-state.md。 - 为主要和次要状态添加预览,然后在 UI 可交互时添加无障碍标签或标识符。当视图需要测试夹具或注入的模拟依赖时,阅读
references/previews.md。 - 通过构建验证:确认无编译器错误,检查预览是否正常渲染,确保状态变化正确传播,并检查列表标识和观察范围不会导致可避免的重新渲染。如果屏幕很大、滚动很多或频繁更新,请阅读
references/performance.md。对于常见的 SwiftUI 编译错误——缺少@State注解、ViewBuilder闭包歧义或泛型类型不匹配——在更新调用点之前解决它们。如果构建失败: 仔细阅读错误消息,修复识别的问题,然后重新构建,再继续下一步。如果预览崩溃,隔离有问题的子视图,确认其状态初始化有效,然后重新运行预览再继续。
组件参考
使用 references/components-index.md 作为入口点。每个组件参考应包括:
- 意图和最佳适用场景。
- 符合本地约定的最小使用模式。
- 陷阱和性能说明。
- 当前仓库中现有示例的路径。
添加新的组件参考
- 创建
references/<component>.md。 - 保持简短且可操作;链接到当前仓库中的具体文件。
- 使用新条目更新
references/components-index.md。






