问题:你的 SwiftUI 应用在真实数据下卡顿或冻结
你构建了一个 SwiftUI 应用,在预览中看起来很棒。设置界面很流畅。小型测试列表滚动完美。然后你将它连接到真实数据源——数百个项目、实时动态或媒体库——一切都崩溃了。
列表滚动时卡顿。网格需要几秒钟才能显示。滚动视图不可预测地跳动。你添加更多数据,应用变得无响应。你尝试将内容包装在 ScrollView 中并添加 ForEach,但性能问题依然存在。
这是开发者在 SwiftUI 中从原型过渡到生产环境时的常见体验。这个框架很强大,但它的布局系统有特定的性能特征,直到你遇到规模问题时才会显现。
为什么会发生这种情况
SwiftUI 的默认堆栈(VStack、HStack、ZStack)是急切渲染的——它们会立即渲染所有子视图,无论它们是否在屏幕上。对于有 10 行的设置界面,这没问题。对于有 1,000 个项目的动态流,这意味着在用户看到任何内容之前就要创建和布局所有 1,000 个视图。
网格也有类似的问题。使用 Grid 包含大量单元格会强制 SwiftUI 提前计算每个单元格的位置。List 更好,因为它会复用行,但配置不正确——比如在惰性容器内放置 GeometryReader——会破坏复用机制,导致每行都被急切测量。
滚动视图增加了另一层复杂性。没有适当的滚动位置跟踪,你就无法实现聊天应用的“滚动到底部”或长文档的“跳转到章节”等功能。默认的 ScrollView 也不能很好地处理混合内容布局——例如将水平芯片选择器与垂直动态流结合在一起。
好的解决方案应该改变什么
一个实用的修复方案应该做三件事:
- 对任何可能增长的集合默认使用惰性加载。 这意味着在滚动视图内使用
LazyVStack而不是VStack,对画廊使用LazyVGrid而不是Grid。 - 为常见布局场景提供清晰的模式。 你不应该猜测是使用
List还是带有惰性堆栈的ScrollView。你应该知道List适用于设置和需要行复用和可访问性的动态流,而带有惰性堆栈的ScrollView适用于自定义布局、水平滚动或混合内容。 - 声明式地处理滚动位置。 你应该能够跟踪滚动位置、以编程方式滚动,并响应滚动事件,而无需编写命令式的
UIScrollView桥接代码。
目标不是学习每个 SwiftUI 布局修改器。而是拥有一小组可靠的模式,你可以将其应用于特定问题:数据驱动的网格、动态流列表、设置界面、搜索界面和滚动驱动的展示。
介绍 SwiftUI 布局组件技能
SwiftUI 布局组件技能是一组针对 iOS 17 及更高版本的 SwiftUI 应用的精选布局和组件模式。它不是你安装的库。它是一个参考文档,你的 AI 编码代理可以使用它来为特定布局问题生成正确、高性能的 SwiftUI 代码。
该技能涵盖堆栈、网格、列表、滚动视图、表单、控件、搜索界面和覆盖层模式。每个模式都包含代码示例、使用时机指导和常见错误避免方法。
实践中的工作方式
当你要求你的 AI 代理构建一个界面时,该技能会为代理提供适合该任务的正确模式。例如:
- “构建一个在 iPhone 和 iPad 上都能工作的照片库” → 代理使用带有
.adaptive列和.aspectRatio单元格的LazyVGrid。 - “创建一个带有分组选项的设置界面” → 代理使用带有
.listStyle(.insetGrouped)和Section块的List。 - “构建一个滚动到最新消息的聊天视图” → 代理使用带有
LazyVStack、.scrollTargetLayout()和ScrollPosition跟踪的ScrollView。 - “为探索界面添加搜索栏” → 代理使用带有
.searchScopes和防抖异步结果的.searchable。
该技能不试图涵盖动画、过渡或计时。这些是独立的关注点。它专注于布局结构和组件配置。
该技能适用的场景
该技能在以下情况下很有用:
- 数据驱动的布局,需要处理数百或数千个项目而不降低性能。
- 集合视图,如照片库、图标选择器或媒体浏览器,需要自适应网格布局。
- 动态流风格的列表,带有下拉刷新、滑动操作或章节标题。
- 设置界面,带有分组控件、开关、选择器和滑块。
- 搜索界面,带有范围搜索、防抖结果和加载状态。
- 滚动驱动的 UI,如聊天视图、分页详情展示或滚动到顶部功能。
- 覆盖层和展示模式,用于工作表、弹出窗口和临时 UI。
如果你正在构建一个只有静态内容的简单两屏应用,你可能不需要这个技能。默认的 SwiftUI 模式会工作得很好。
该技能涵盖的内容
堆栈布局
该技能区分了急切堆栈(VStack、HStack、ZStack)用于小型、固定内容,以及惰性堆栈(LazyVStack、LazyHStack)用于滚动视图内的大型或动态集合。它解释了何时使用每种堆栈,并为两者提供了代码示例。
网格布局
它涵盖了带有 .adaptive 列(用于跨设备尺寸缩放的布局)和 .flexible 列(用于固定列数)的 LazyVGrid。它解释了如何使用 .aspectRatio 进行单元格大小调整,并警告不要在惰性容器内放置 GeometryReader,这会强制急切测量并破坏惰性加载。
列表模式
该技能提供了带有 .listStyle(.plain) 的动态流风格列表和带有 .listStyle(.insetGrouped) 的设置列表的模式。它涵盖了使用 .listRowInsets 和 .listRowSeparator 的行自定义、使用 .refreshable 的下拉刷新和滑动操作。它还解释了何时使用 List 与带有惰性堆栈的 ScrollView——List 用于内置行复用和可访问性,ScrollView 用于自定义布局和水平滚动。
ScrollView 和 ScrollPosition
它涵盖了带有惰性堆栈的 ScrollView 用于自定义布局和水平滚动。它解释了 ScrollPosition 用于声明式、双向滚动位置跟踪和程序化滚动。它展示了如何使用 .scrollTargetLayout() 和稳定目标进行可靠的跳转到 ID 行为。它还涵盖了 safeAreaInset(edge:) 用于在键盘上方固定内容。
表单和控件
该技能提供了带有 Section 块、分组控件和 .formStyle(.grouped) 的 Form 模式。它涵盖了 Toggle、Picker、Slider、DatePicker 和 TextField,以及与 @State、@Binding 或 @AppStorage 的正确绑定。它解释了如何在输入密集型表单中使用 @FocusState 进行键盘焦点管理。
可搜索
它涵盖了用于添加原生搜索 UI 的 .searchable、用于多种搜索模式的 .searchScopes,以及用于防抖异步结果的 .task(id:)。它展示了如何处理加载状态和空结果。
覆盖层和展示
该技能涵盖了用于工作表、弹出窗口和临时 UI 的覆盖层模式。它解释了如何使用带有适当状态管理的 .sheet、.popover 和 .confirmationDialog。
能力边界
该技能有明确的边界:
- 它仅涵盖布局和组件模式。 它不涵盖动画、过渡或计时。这些由单独的技能处理。
- 它针对 iOS 17 及更高版本。 一些模式(如
.scrollEdgeEffectStyle)需要 iOS 26,但大多数向后兼容到 iOS 17。 - 它不涵盖自定义绘图或图形。 对于这些,你需要不同的技能。
- 它不涵盖数据层集成。 它假设你已经解决了数据源问题,并专注于如何显示数据。
何时不使用该技能
在以下情况下不要使用该技能:
- 你正在构建一个每个屏幕少于 20 个项目的简单静态内容应用。
- 你需要重度自定义动画或复杂的过渡逻辑。
- 你的目标是 iOS 16 或更早版本。
- 你需要绘制自定义形状或使用 Metal 进行图形处理。
设置和安全
该技能是一个参考文档,而不是库。没有什么需要安装的。你的 AI 代理读取技能文件并在生成代码时应用这些模式。
该技能是 dpearson2699 的 swift-ios-skills 仓库的一部分。该仓库有 931 个星标,涵盖了多个 iOS 开发主题,包括 SwiftUI、SwiftData、MapKit 和 StoreKit。许可证是宽松的。
该技能不执行代码或访问外部服务。它是一个静态参考文档。除了它描述的代码模式固有的安全问题外,没有其他安全问题。
仓库信号
该仓库正在积极维护,涵盖了广泛的 iOS 开发主题。931 个星标表明了社区的兴趣和验证。该技能是更大集合的一部分,因此你可以将其与其他技能结合用于网络、数据持久化或地图集成。
使用前检查
在依赖该技能之前,请检查:
- 你的 AI 代理是否支持技能文件? 该技能专为 Claude Code、Cursor 和 Codex 等可以读取参考文档的代理设计。
- 你的目标 iOS 版本是否兼容? 大多数模式适用于 iOS 17+,但有些需要 iOS 26。
- 你是否特别需要布局模式? 如果你的问题是动画、网络或数据持久化,不同的技能会更合适。
你可以在 SwiftUI 布局组件技能页面上查看完整的技能文档和代码示例。