做 iOS 客户端这几年,我发现自己写的页面里,SwiftUI 的 Form 出现频率是真的高。设置页、个人资料编辑、筛选条件、审批录入……凡是那种"一行一行收集信息"的界面,Form 基本都是首选。它给我的感觉就像一个自带精装修的毛坯房,你只需要把家具摆进去,系统会帮你把墙体、地板、圆角、分隔线全部处理妥当。这篇文章不聊官网上那些 Hello World,就聊我把 Form 用进生产项目后的实战经验:它免费赠送的能力有哪些、常用控件怎么组合、动态数据怎么绑,以及几个文档里根本没写、但不踩不行的坑。
1. 为什么建议直接上 Form,而不是手搓 ScrollView + VStack
先讲一段我自己交过学费的经历。早期写一个账号设置页,我习惯用 ScrollView 包 VStack,再手动给每行加背景色、加圆角、控制分隔线。前两版需求简单,改起来也算顺手。直到某次版本迭代,产品要求在设置项超过一屏时,底部悬浮一个"保存修改"按钮。手搓方案当场崩溃:ScrollView 内容高度撑开之后,safe area 要自己算、键盘弹起时内容被顶掉的 inset 要自己处理、分隔线与分组圆角的间距在不同机型上还对不齐。那版改完我彻底弃坑,所有类似页面全部换成 Form。
Form 免费送你的东西,比大部分人想象中多:
- 自动分组视觉。
Section天然带毛玻璃质感的圆角卡片背景,多个分组之间自动留白,不需要你手动画圆角矩形。 - 系统级
insetGrouped样式。在 iOS 上它默认采用列表的 insetGrouped 外观,跟系统设置 App 观感一致,用户看着就眼熟。 - 键盘与滚动联动。当键盘弹起时,
Form所在的滚动区域会自动调整 inset,当前聚焦的输入框基本不会被挡住,这个手搓 ScrollView 至少要多写十几行代码。 - 静态内容自动撑开。不需要手动设置
VStack的宽高,Form自己是滚动容器,内容多时自动滚,内容少时也不会出现空白跳动。
不过 Form 也不是万能钥匙。我目前的判断标准是:如果页面是"结构化地收集少量信息,且希望系统统一外观",直接 Form;如果页面是"复杂卡片式信息流、瀑布流、需要高度自定义 cell 布局",那就用 LazyVGrid 或 List 自绘行;如果表单项超过一两百个(比如超长配置项集合),Form 的静态视图树渲染成本会偏高,很长列表还是 List + LazyVStack 兜底更稳。日常业务场景里,一个设置页、资料页基本在 10 到 30 行之间,Form 的性能完全不需要担心。
还有一个容易被忽略的优势:Form 对辅助功能(VoiceOver)的支持是系统级的。手搓列表时,每行的 accessibility 元素顺序、是否合并成单个元素、焦点朗读顺序都要手动维护,而 Form 会根据 Section 结构自动组织朗读顺序。这一点在接无障碍需求时能省不少事。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Form 内建控件的组合套路:从设置页到复杂录入
Form 的骨架是 Section。我的习惯是:一个 Section 只放一类事情。比如通知设置页,我会把"消息通知总开关"放在第一个 Section,"具体哪些事件推送"放第二个 Section,"推送时是否显示详情"放第三个 Section。这样分组的视觉锚点非常清楚,用户扫一眼就能理解层级。
给一个偏完整的示例,这是一个"消息通知设置"页的核心结构:
swift复制Form {
Section {
Toggle("接收推送通知", isOn: $pushEnabled)
} header: {
Text("总开关")
} footer: {
Text("关闭后不再接收任何推送消息")
}
if pushEnabled {
Section {
Toggle("评论提醒", isOn: $commentNotify)
Toggle("点赞提醒", isOn: $likeNotify)
Toggle("私信提醒", isOn: $messageNotify)
} header: {
Text("事件类型")
}
}
Section {
Picker("提醒时段", selection: $period) {
Text("全天").tag(0)
Text("仅白天").tag(1)
Text("仅夜间").tag(2)
}
.pickerStyle(.menu)
} header: {
Text("推送时段")
}
Section {
Stepper("保留天数: \(keepDays) 天", value: $keepDays, in: 1...90)
} footer: {
Text("超过该天数的推送记录将被自动清理")
}
}
这里面有几个值得说明的细节。
Toggle 直接绑定 Bool 即可,系统会自动展示开关样式。Stepper 绑定数值,文案里实时显示当前值,用户操作时有明确的即时反馈。Picker 在 Form 里有几种表现形态,默认样式在外层没有 `NavigationLi
