SwiftUI专家技能
概览
使用这项技能构建、审查或改进SwiftUI特性,确保正确的状态管理、现代API使用、Swift并发最佳实践、最优视图组合以及iOS 26+液态玻璃风格。优先考虑原生API、苹果设计指南和性能意识模式。这项技能专注于事实和最佳实践,而不是强制执行特定的架构模式。
工作流程决策树
1) 审查现有的SwiftUI代码
- 根据选择指南检查属性包装器使用情况(见
references/state-management.md) - 验证现代API使用情况(见
references/modern-apis.md) - 验证视图组合是否遵循提取规则(见
references/view-structure.md) - 检查是否应用了性能模式(见
references/performance-patterns.md) - 验证列表模式使用稳定身份(见
references/list-patterns.md) - 检查液态玻璃使用的准确性和一致性(见
references/liquid-glass.md) - 验证iOS 26+可用性处理和合理的回退
2) 改进现有的SwiftUI代码
- 审计状态管理以正确选择包装器(优先选择
@Observable而不是ObservableObject) - 用现代等价物替换过时的API(见
references/modern-apis.md) - 将复杂视图提取到单独的子视图中(见
references/view-structure.md) - 重构热路径以最小化冗余状态更新(见
references/performance-patterns.md) - 确保ForEach使用稳定身份(见
references/list-patterns.md) - 建议在
UIImage(data:)使用时进行图像降采样(作为可选优化,见references/image-optimization.md) - 仅在用户明确请求时采用液态玻璃
3) 实施新的SwiftUI特性
- 首先设计数据流:确定拥有与注入状态(见
references/state-management.md) - 使用现代API(不使用过时的修饰符或模式,见
references/modern-apis.md) - 对于共享状态使用
@Observable(如果不使用默认actor隔离,则使用@MainActor) - 为最优diffing结构化视图(早期提取子视图,保持视图小,见
references/view-structure.md) - 将业务逻辑分离到可测试的模型中(见
references/layout-best-practices.md) - 在布局/外观修饰符后应用玻璃效果(见
references/liquid-glass.md) - 使用
#available限制iOS 26+特性,并提供回退
核心指南
状态管理
- 对于新代码,始终优先选择
@Observable而不是ObservableObject - 除非使用默认actor隔离,否则用
@MainActor标记@Observable类 - 始终将
@State和@StateObject标记为private(使依赖关系清晰) - 永远不要将传递值声明为
@State或@StateObject(它们只接受初始值) - 与
@Observable类一起使用@State(而不是@StateObject) - 仅当子视图需要修改父状态时使用
@Binding - 需要绑定的注入
@Observable对象使用@Bindable - 将只读值用于
let;将var+.onChange()用于响应式读取 - 遗留:
@StateObject用于拥有的ObservableObject;@ObservedObject用于注入的 - 嵌套
ObservableObject不起作用(直接将嵌套对象传递给子视图);@Observable很好地处理嵌套
现代API
- 使用
foregroundStyle()而不是foregroundColor() - 使用
clipShape(.rect(cornerRadius:))而不是cornerRadius() - 使用
TabAPI而不是tabItem() - 使用
Button而不是onTapGesture()(除非需要位置/计数) - 使用
NavigationStack而不是NavigationView - 使用
navigationDestination(for:)进行类型安全导航 - 使用双参数或无参数
onChange()变体 - 使用
ImageRenderer渲染SwiftUI视图 - 使用
.sheet(item:)而不是.sheet(isPresented:)进行基于模型的内容 - 表单应拥有自己的操作并内部调用
dismiss() - 使用
ScrollViewReader进行稳定ID的程序滚动 - 避免使用
UIScreen.main.bounds进行尺寸调整 - 避免
GeometryReader,当存在替代方案时(例如,containerRelativeFrame())
Swift最佳实践
- 使用现代文本格式化(
.format参数,而不是String(format:)) - 使用
localizedStandardContains()进行用户输入过滤(而不是contains()) - 优先使用静态成员查找(
.blue与Color.blue) - 使用
.task修饰符自动取消异步工作 - 使用
.task(id:)进行值依赖任务
视图组合
- 对于状态变化,优先选择修饰符而不是条件视图(保持视图身份)
- 将复杂视图提取到单独的子视图中,以提高可读性和性能
- 保持视图小以获得最佳性能
- 保持视图
body简单纯净(无副作用或复杂逻辑) - 仅对小型、简单的部分使用
@ViewBuilder函数 - 优先选择
@ViewBuilder let content: Content而不是基于闭包的内容属性 - 将业务逻辑分离到可测试的模型中(不是关于强制执行架构)
- 动作处理程序应引用方法,而不是包含内联逻辑
- 使用相对布局而不是硬编码常数
- 视图应在任何上下文中工作(不假设屏幕尺寸或呈现样式)
性能
- 仅将所需值传递给视图(避免大型“配置”或“上下文”对象)
- 消除不必要的依赖以减少更新扇出
- 在热路径中分配状态之前检查值变化
- 避免在
onReceive、onChange、滚动处理程序中进行冗余状态更新 - 在频繁执行的代码路径中最小化工作
- 对于大型列表使用
LazyVStack/LazyHStack - 确保ForEach使用稳定身份(从不使用
.indices进行动态内容) - 确保ForEach元素的视图数量恒定
- 避免在ForEach中进行内联过滤(预过滤和缓存)
- 避免在列表行中使用
AnyView - 考虑使用POD视图进行快速diffing(或将昂贵的视图包装在POD父视图中)
- 建议在遇到
UIImage(data:)时进行图像降采样(作为可选优化) - 避免布局抖动(深层层次结构、过度
GeometryReader) - 通过阈值限制频繁的几何更新
- 使用
Self._printChanges()调试意外视图更新
液态玻璃(iOS 26+)
仅在用户明确请求时采用。
- 使用原生
glassEffect、GlassEffectContainer和玻璃按钮样式 - 在
GlassEffectContainer中包装多个玻璃元素 - 在布局和视觉修饰符后应用
.glassEffect() - 仅对可点击/可聚焦的元素使用
.interactive() - 使用
glassEffectID与@Namespace进行变形过渡
快速参考
属性包装器选择(现代)
| 包装器 | 使用时 |
|---|---|
@State |
内部视图状态(必须是private),或拥有的@Observable类 |
@Binding |
子视图修改父状态 |
@Bindable |
需要绑定的注入@Observable对象 |
let |
来自父视图的只读值 |
var |
通过.onChange()观察的只读值 |
遗留(iOS 17之前):
| 包装器 | 使用时 |
|---|---|
@StateObject |
视图拥有一个ObservableObject(使用@State与@Observable代替) |
@ObservedObject |
视图接收一个ObservableObject |
现代API替代品
| 过时的 | 现代替代品 |
|---|---|
foregroundColor() |
foregroundStyle() |
cornerRadius() |
clipShape(.rect(cornerRadius:)) |
tabItem() |
Tab API |
onTapGesture() |
Button(除非需要位置/计数) |
NavigationView |
NavigationStack |
onChange(of:) { value in } |
onChange(of:) { old, new in }或onChange(of:) { } |
fontWeight(.bold) |
bold() |
GeometryReader |
containerRelativeFrame()或visualEffect() |
showsIndicators: false |
.scrollIndicators(.hidden) |
String(format: "%.2f", value) |
Text(value, format: .number.precision(.fractionLength(2))) |
string.contains(search) |
string.localizedStandardContains(search)(用于用户输入) |
液态玻璃模式
// 基本玻璃效果及回退
if #available(iOS 26, *) {
content
.padding()
.glassEffect(.regular.interactive(), in: .rect(cornerRadius: 16))
} else {
content
.padding()
.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 16))
}
// 分组玻璃元素
GlassEffectContainer(spacing: 24) {
HStack(spacing: 24) {
GlassButton1()
GlassButton2()
}
}
// 玻璃按钮
Button("Confirm") { }
.buttonStyle(.glassProminent)
审查清单
状态管理
- [ ] 对于新代码使用
@Observable而不是ObservableObject - [ ] 用
@MainActor标记@Observable类(如果需要) - [ ] 与
@Observable类一起使用@State(而不是@StateObject) - [ ]
@State和@StateObject属性是private - [ ] 传递值未声明为
@State或@StateObject - [ ] 仅在子视图修改父状态时使用
@Binding - [ ] 需要绑定的注入
@Observable使用@Bindable - [ ] 避免嵌套
ObservableObject(或直接传递给子视图)
现代API(见references/modern-apis.md)
- [ ] 使用
foregroundStyle()而不是foregroundColor() - [ ] 使用
clipShape(.rect(cornerRadius:))而不是cornerRadius() - [ ] 使用
TabAPI而不是tabItem() - [ ] 使用
Button而不是onTapGesture()(除非需要位置/计数) - [ ] 使用
NavigationStack而不是NavigationView - [ ] 避免使用
UIScreen.main.bounds - [ ] 尽可能使用
GeometryReader的替代品 - [ ] 按钮图像包括文稿标签以提高可访问性
表单和导航(见references/sheet-navigation-patterns.md)
- [ ] 使用
.sheet(item:)进行基于模型的表单 - [ ] 表单拥有自己的操作并内部取消
- [ ] 使用
navigationDestination(for:)进行类型安全导航
ScrollView(见references/scroll-patterns.md)
- [ ] 使用稳定ID的
ScrollViewReader进行程序滚动 - [ ] 使用
.scrollIndicators(.hidden)而不是初始化参数
文本和格式化(见references/text-formatting.md)
- [ ] 使用现代文本格式化(不是
String(format:)) - [ ] 使用
localizedStandardContains()进行搜索过滤
视图结构(见references/view-structure.md)
- [ ] 对于状态变化,使用修饰符而不是条件
- [ ] 将复杂视图提取到单独的子视图中
- [ ] 保持视图小以获得最佳性能
- [ ] 容器视图使用
@ViewBuilder let content: Content
性能(见references/performance-patterns.md)
- [ ] 视图
body保持简单纯净(无副作用) - [ ] 仅传递所需值(不是大型配置对象)
- [ ] 消除不必要的依赖
- [ ] 在分配状态之前检查值变化
- [ ] 热路径最小化状态更新
- [ ] 在
body中不创建对象 - [ ] 将繁重的计算移出
body
列表模式(见references/list-patterns.md)
- [ ] ForEach使用稳定身份(不是
.indices) - [ ] ForEach元素的视图数量恒定
- [ ] ForEach中不进行内联过滤
- [ ] 列表行中不使用
AnyView
布局(见references/layout-best-practices.md)
- [ ] 避免布局抖动(深层层次结构,过度
GeometryReader) - [ ] 通过阈值限制频繁的几何更新
- [ ] 将业务逻辑分离到可测试的模型中
- [ ] 动作处理程序引用方法(不是内联逻辑)
- [ ] 使用相对布局(不是硬编码常数)
- [ ] 视图在任何上下文中工作(上下文不可知)
液态玻璃(iOS 26+)
- [ ] 使用
#available(iOS 26, *)及液态玻璃的回退 - [ ] 多个玻璃视图包装在
GlassEffectContainer中 - [ ] 在布局/外观修饰符后应用
.glassEffect() - [ ]
.interactive()仅用于用户交互元素 - [ ] 相关元素的形状和色调保持一致
参考资料
references/state-management.md- 属性包装器和数据流(优先选择@Observable)references/view-structure.md- 视图组合、提取和容器模式references/performance-patterns.md- 性能优化技术和反模式references/list-patterns.md- ForEach身份、稳定性和列表最佳实践references/layout-best-practices.md- 布局模式、上下文不可知视图和可测试性references/modern-apis.md- 现代API使用和过时替代品references/sheet-navigation-patterns.md- 表单呈现和导航模式references/scroll-patterns.md- ScrollView模式和程序滚动references/text-formatting.md- 现代文本格式化和字符串操作references/image-optimization.md- AsyncImage、图像降采样和优化references/liquid-glass.md- iOS 26+液态玻璃API
哲学
这项技能专注于事实和最佳实践,而不是架构观点:
- 我们不强制执行特定的架构(例如,MVVM、VIPER)
- 我们鼓励将业务逻辑分离以进行可测试性
- 我们优先考虑现代API而不是过时的API
- 我们强调使用
@MainActor和@Observable进行线程安全 - 我们针对性能和可维护性进行优化
- 我们遵循苹果的人类界面指南和API设计模式