iOS App 从 0 到 1

SwiftUI 界面与布局(组件手册)

按使用频率讲解布局、控件、修饰符;既能顺着学,也能当 API 速查。

1怎么用这一章(教程 + API 手册)

先按顺序过一遍「高频」组件,能拼出大多数界面;以后忘了参数,用侧栏搜索或本页目录跳到对应卡片当 API 查。

常用几乎天天写,优先背熟 偶尔知道有、选型时再查
想做…先看频率
竖/横/叠布局布局容器常用
标题、正文、图标Text / Image常用
按钮、输入、开关控件常用
边距、圆角、颜色修饰符速查常用
列表 / 导航本章点到 + 第 07 章常用
表单、选择器Form / Picker常用
网格、懒加载栈Lazy* / Grid偶尔
SwiftUI 写法口诀:容器包内容 + 修饰符链式改外观。改数据用第 05 章状态;本章专注「长什么样」。

2布局容器(完整参数手册)

下面按「初始化参数 → 每个取值含义 → 示例」写全,方便当 API 查。90% 界面仍是三大 Stack + Spacer。

约定:参数名后的类型是 Swift 类型;「默认」表示你不写这个参数时系统用的值。
VStack 常用
何时用

子视图从上到下排列。页面骨架、表单区块、标题+正文最常用。≈ Compose Column / ArkUI Column。

初始化签名
VStack(
  alignment: HorizontalAlignment = .center,
  spacing: CGFloat? = nil,
  @ViewBuilder content: () -> Content
)
参数一览(写全)
参数类型默认含义
alignmentHorizontalAlignment.center子视图在水平方向如何对齐(因为竖排时,左右才是「对齐轴」)
spacingCGFloat?nil相邻子视图之间的竖直间距(单位 pt)。nil = 用系统默认间距;写 0 则紧贴;写 12 则固定 12pt
content@ViewBuilder 闭包(必填)花括号 { ... } 里放子视图,可写多个
alignment 全部取值(HorizontalAlignment)
取值效果(LTR 中文/英文界面)什么时候用
.leading子视图整体靠左(前缘对齐)标题+正文、表单标签,最常用
.center子视图水平居中(默认)欢迎页、空状态、居中按钮组
.trailing子视图整体靠右(后缘对齐)右对齐金额、操作区
记法:VStack 的 alignment 管的是「左右」;子视图自己的宽度不同时,才能看出 leading/trailing 差别。
示例
VStack(alignment: .leading, spacing: 12) {
  Text("标题").font(.title.bold())
  Text("副标题").foregroundStyle(.secondary)
  Button("开始") { }
}
.padding()

// spacing: nil → 系统默认间距
VStack { Text("A"); Text("B") }

// spacing: 0 → 紧贴
VStack(spacing: 0) { Text("A"); Text("B") }
HStack 常用
何时用

子视图从左到右排列。图标+文字、底栏、一行信息。≈ Compose Row / ArkUI Row。

初始化签名
HStack(
  alignment: VerticalAlignment = .center,
  spacing: CGFloat? = nil,
  @ViewBuilder content: () -> Content
)
参数一览(写全)
参数类型默认含义
alignmentVerticalAlignment.center子视图在竖直方向如何对齐(横排时,上下才是对齐轴)
spacingCGFloat?nil相邻子视图之间的水平间距。nil 默认;0 紧贴;8 固定 8pt
content@ViewBuilder(必填)花括号里的子视图
alignment 全部取值(VerticalAlignment)
取值效果什么时候用
.top子视图顶部对齐左边大图、右边多行文字时,文字贴顶
.center垂直居中(默认)图标和单行文字并排,最常用
.bottom子视图底部对齐底部基线对齐的装饰
.firstTextBaseline按第一行文字基线对齐不同字号的 Text 并排,要「字坐在同一条线上」
.lastTextBaseline按最后一行文字基线对齐多行文本与旁边元素底对齐(少用)
示例
HStack(alignment: .center, spacing: 8) {
  Image(systemName: "star.fill")
  Text("收藏")
  Spacer()           // 把右边推开,见 Spacer
  Text("12")
}

// 大标题 + 小标注:用基线对齐更整齐
HStack(alignment: .firstTextBaseline, spacing: 4) {
  Text("¥99").font(.largeTitle.bold())
  Text("起").font(.caption).foregroundStyle(.secondary)
}
ZStack 常用
何时用

子视图叠在一起(Z 轴)。写在后面的在上层。典型:背景+内容、头像角标、卡片底+文字。≈ Compose Box / ArkUI Stack。

初始化签名
ZStack(
  alignment: Alignment = .center,
  @ViewBuilder content: () -> Content
)

注意:ZStack 没有 spacing 参数(叠放不谈间距;要用 padding/offset 自己调)。

参数一览(写全)
参数类型默认含义
alignmentAlignment.center所有子视图在 ZStack 区域内的默认停靠位置。某个子视图还可用自己的 .frame(alignment:) / padding 微调
content@ViewBuilder(必填)花括号里的子视图;越靠后越在上
alignment 全部取值(Alignment)——九宫格

可以想成手机屏幕上的九个停靠点(中文 LTR):

取值位置常见用途
.topLeading上 + 左(左上角)关闭按钮、返回附近的角标
.top上 + 水平居中顶部横幅、顶部标题条
.topTrailing上 + 右(右上角)NEW / 红点角标(高频)
.leading竖直居中 + 左左侧竖条、左边装饰
.center正中(默认)背景上居中放主内容
.trailing竖直居中 + 右右侧箭头、右侧操作
.bottomLeading下 + 左(左下角)左下说明文字
.bottom下 + 水平居中底部指示器、底部按钮条
.bottomTrailing下 + 右(右下角)悬浮加号、右下角角标
alignment 管的是「默认停在哪一角」;真正把角标挪开一点,通常再给角标加 .padding(8)。
示例
// 右上角角标:alignment 用 .topTrailing
ZStack(alignment: .topTrailing) {
  // 下层:背景(先写)
  RoundedRectangle(cornerRadius: 12)
    .fill(.blue.opacity(0.15))
    .frame(height: 120)

  // 中层:主内容
  Text("卡片内容")
    .padding()

  // 上层:角标(后写 → 在最上面)
  Text("NEW")
    .font(.caption2.bold())
    .padding(6)
    .background(.red)
    .foregroundStyle(.white)
    .clipShape(Capsule())
    .padding(8)   // 别贴死边缘
}

// 默认居中叠放
ZStack {
  Circle().fill(.orange).frame(width: 80, height: 80)
  Text("42").bold().foregroundStyle(.white)
}
Spacer 常用
何时用

在 Stack 里贪婪占据剩余空间,把兄弟视图推到两边/两端。

初始化签名
Spacer(minLength: CGFloat? = nil)
参数一览(写全)
参数类型默认含义
minLengthCGFloat?nilSpacer 被压缩时的最小长度。nil 时系统用默认最小(可接近 0);写 8 表示再挤也不能小于 8pt
行为说明
  • 在 HStack 里:水平方向撑开(左内容 | Spacer | 右内容)
  • 在 VStack 里:竖直方向撑开(上内容 / Spacer / 下内容)
  • 在 ZStack 里:一般没什么用(叠放不分配轴上剩余空间)
  • 多个 Spacer:大致均分剩余空间
HStack {
  Text("左")
  Spacer()                 // 尽量撑开
  Text("右")
}
HStack {
  Text("左")
  Spacer(minLength: 24)    // 最少留 24pt 缝
  Text("右")
}
Divider 常用
何时用

画一条系统风格的细分割线。

初始化签名
Divider()
参数一览

无初始化参数。外观靠修饰符或环境间接影响:

常见改法作用
(默认)跟随系统分隔线颜色
.background(.red) 等(效果因系统版本而异,不如 overlay 直观)尝试改色
自制:Rectangle().fill(.quaternary).frame(height: 1)完全自定义细线
VStack {
  Text("上")
  Divider()
  Text("下")
}
ScrollView 常用
何时用

内容可能超出屏幕,需要滚动查看。

初始化签名(常用重载)
ScrollView(
  _ axes: Axis.Set = .vertical,
  showsIndicators: Bool = true,
  @ViewBuilder content: () -> Content
)
参数一览(写全)
参数类型默认含义
axesAxis.Set.vertical允许滚动的方向,见下表
showsIndicatorsBooltrue是否显示滚动条指示器。false = 隐藏(仍可滚)
content@ViewBuilder(必填)被滚动的内容
axes 全部常用取值
取值含义
.vertical只能上下滚(默认);可省略不写
.horizontal只能左右滚
[.horizontal, .vertical] 或 .all(视版本写法)两个方向都能滚(少用,注意嵌套手势冲突)
ScrollView {                 // 默认竖直
  VStack { /* 很长内容 */ }
}
ScrollView(.horizontal, showsIndicators: false) {
  HStack { /* 横向卡片 */ }
}
上千行数据用 List / LazyVStack,不要 ScrollView + VStack 一次创建全部。
LazyVStack / LazyHStack 偶尔
何时用

和 VStack/HStack 排列方式相同,但懒加载:只创建靠近可见区域的子视图。必须放在可滚动容器(通常是 ScrollView)里。

初始化签名
LazyVStack(
  alignment: HorizontalAlignment = .center,
  spacing: CGFloat? = nil,
  pinnedViews: PinnedScrollableViews = .init(),
  @ViewBuilder content: () -> Content
)

LazyHStack(
  alignment: VerticalAlignment = .center,
  spacing: CGFloat? = nil,
  pinnedViews: PinnedScrollableViews = .init(),
  @ViewBuilder content: () -> Content
)
参数一览(写全)
参数类型默认含义
alignment同 VStack / HStack.center对齐方式;取值表见上文 VStack/HStack
spacingCGFloat?nil子视图间距
pinnedViewsPinnedScrollableViews空滚动时是否钉住 Section 的 header/footer。常用:.sectionHeaders、.sectionFooters、或二者组合
content@ViewBuilder(必填)子视图;常配合 ForEach
ScrollView {
  LazyVStack(alignment: .leading, spacing: 8, pinnedViews: [.sectionHeaders]) {
    ForEach(0..<200, id: \.self) { i in
      Text("行 \(i)").padding()
    }
  }
}
LazyVGrid / LazyHGrid 偶尔
何时用

网格(宫格、相册)。竖向滚动网格用 LazyVGrid;横向用 LazyHGrid。

初始化签名(LazyVGrid)
LazyVGrid(
  columns: [GridItem],
  alignment: HorizontalAlignment = .center,
  spacing: CGFloat? = nil,
  pinnedViews: PinnedScrollableViews = .init(),
  @ViewBuilder content: () -> Content
)
参数一览(写全)
参数类型默认含义
columns(V)/ rows(H)[GridItem](必填)列(或行)怎么分,见下表 GridItem
alignment水平/垂直对齐.center格子内容对齐
spacingCGFloat?nil行与行(或列与列)间距
pinnedViewsPinnedScrollableViews空是否钉住 section 头/尾
content@ViewBuilder(必填)每个格子的内容
GridItem 尺寸模式(写全)
写法含义
GridItem(.flexible(minimum:maximum:))弹性列:在最小~最大之间平分剩余宽度。只写 .flexible() 最常见
GridItem(.adaptive(minimum:maximum:))自适应:按 minimum 自动算能排几列(宫格常用)
GridItem(.fixed(80))固定 80pt 宽(或高)
GridItem(..., spacing: 8, alignment: .center)该项与下一项的间距、格子内对齐
// 固定 3 列
let three = Array(repeating: GridItem(.flexible(), spacing: 12), count: 3)

// 最小 100pt,能排几列排几列
let adaptive = [GridItem(.adaptive(minimum: 100), spacing: 12)]

ScrollView {
  LazyVGrid(columns: adaptive, spacing: 12) {
    ForEach(0..<12, id: \.self) { i in
      RoundedRectangle(cornerRadius: 8)
        .fill(.blue.opacity(0.2))
        .frame(height: 80)
        .overlay(Text("\(i)"))
    }
  }
  .padding()
}

3Text 与字体

Text 常用
何时用

展示只读文字(用户不能直接编辑;要输入用 TextField)。最常见写法:

  • Text("你好") —— 固定文案
  • Text("你好,\(name)") —— 字符串插值,把变量嵌进文字
常用修饰符(新手必懂)

下面每一项都是「点在 Text 后面」的链式修饰符:改字体、颜色、行数等。不写则用系统默认。

.font(...) —— 字体大小 / 样式档位

决定字有多大、像不像标题。系统提供语义档位(跟动态字体、无障碍缩放兼容),也支持写死字号。

写法什么意思典型场景
.font(.largeTitle)最大号标题欢迎页大标题
.font(.title) / .title2 / .title3一级~三级标题,依次略小页面标题、卡片标题
.font(.headline)偏粗的强调正文列表行主标题
.font(.body)默认正文字号(不写 font 时也接近这个)普通说明文字
.font(.callout) / .subheadline略小于正文次要说明
.font(.footnote) / .caption / .caption2更小,caption2 最小档之一角标、时间戳、辅助信息
.font(.system(size: 18))写死 18pt(不太跟系统动态字体走)必须精确对齐设计稿时
.font(.system(size: 18, weight: .semibold, design: .rounded))字号 + 粗细 + 字形风格(圆体等)品牌感强的数字/标题
新手优先用 .title / .body / .caption 这类语义档,少写死 size:,这样用户开「更大字体」时你的 App 也更跟手。

.fontWeight(.bold) / .bold() —— 加粗

只改笔画粗细,不改字号档位。

  • .bold():快捷写法,等于粗体,最常用
  • .fontWeight(.regular):常规
  • .fontWeight(.medium) / .semibold / .bold / .heavy:越来越粗

也可以和 font 写在一起:.font(.title.bold()) 或 .font(.title).bold(),效果都是「标题档 + 加粗」。

Text("普通")
Text("加粗").bold()
Text("半粗").fontWeight(.semibold)
Text("标题加粗").font(.title.bold())

.foregroundStyle(...) —— 文字颜色 / 前景样式

控制字「涂什么色」。老 API 叫 .foregroundColor,新项目用 foregroundStyle。

写法什么意思
.foregroundStyle(.primary)主文字色(浅色模式接近黑,深色模式接近白),默认观感
.foregroundStyle(.secondary)次要灰字,用来写说明、弱化信息
.foregroundStyle(.tertiary)更淡,辅助信息
.foregroundStyle(.red) / .blue 等系统色
.foregroundStyle(Color(red:0, green:0.4, blue:1))自定义 RGB
.foregroundStyle(.white)白字(常配深色背景)
Text("主标题")                          // 默认 primary
Text("说明文字").foregroundStyle(.secondary)
Text("错误").foregroundStyle(.red)

.multilineTextAlignment(...) —— 多行时怎么对齐

只影响多行 Text内部各行的对齐(单行时几乎看不出差别)。

  • .leading:靠前(中文/英文 LTR 下是左对齐)——说明文字最常用
  • .center:居中——空状态、欢迎语常用
  • .trailing:靠后(右对齐)——较少
它不是把整个 Text 控件移到屏幕左边。要把控件本身靠左,用外层 VStack(alignment: .leading) 或 .frame(maxWidth: .infinity, alignment: .leading)。
Text("这是一段比较长的说明,会自动换行。换行后每一行都左对齐。")
  .multilineTextAlignment(.leading)
  .frame(width: 200)   // 限制宽度才会换行,方便看出效果

.lineLimit(...) —— 最多显示几行

限制文本最多占几行;超出部分会被截断(默认末尾 …)。

  • .lineLimit(1):只显示一行(列表副标题常用)
  • .lineLimit(2):最多两行
  • .lineLimit(nil) 或不写:不限制,有多少行显示多少行
  • .lineLimit(1...3):至少 1 行、最多 3 行(较新系统)
Text(longArticle)
  .lineLimit(2)   // 再长也只显示两行,后面变省略号

.truncationMode(...) —— 超长时省略号放哪

和 lineLimit 一起用:决定「砍掉」的位置。

  • .tail(最常用):省略号在末尾 → 很长的标题…
  • .middle:省略号在中间 → 很长…标题(文件名有时用)
  • .head:省略号在开头 → …很长的标题(少见)
Text("SwiftUI界面与布局完全入门指南")
  .lineLimit(1)
  .truncationMode(.tail)    // 末尾 …
  // .truncationMode(.middle) // 中间 …
串起来的完整示例
VStack(alignment: .leading, spacing: 8) {
  // 大标题:大号 + 加粗
  Text("今日待办")
    .font(.title.bold())

  // 说明:正文字号 + 灰色次要色 + 最多 3 行 + 多行左对齐
  Text("这里是说明文字,可能会很长很长,超出三行会变成省略号。")
    .font(.body)
    .foregroundStyle(.secondary)
    .multilineTextAlignment(.leading)
    .lineLimit(3)
    .truncationMode(.tail)

  // 插值:把数字嵌进字符串;等宽数字更整齐
  Text("未完成 \(3) 项")
    .font(.title3.monospacedDigit())
    .foregroundStyle(.primary)
}
.padding()
记忆顺序:先定 font(多大)→ 再 bold/fontWeight(多粗)→ 再 foregroundStyle(什么色)→ 需要换行/截断再加 multilineTextAlignment / lineLimit / truncationMode。
Label 常用
何时用

系统推荐的「图标+标题」组合,辅助功能更友好。

初始化签名(常用)
Label("标题", systemImage: "gear")
Label("标题", image: "AssetName")          // Assets 图片
Label { Text("标题") } icon: { Image(...) } // 完全自定义
参数一览
参数含义
标题显示的文字
systemImageSF Symbol 名字(如 gearshape)
image资源目录里的图片名
title / icon 闭包自定义标题视图与图标视图
.labelStyle 全部常用取值
取值含义
.automatic随环境自动(默认)
.titleAndIcon图标 + 标题都显示
.titleOnly只显示标题
.iconOnly只显示图标(工具栏常用)
示例
Label("设置", systemImage: "gearshape.fill")
Label("仅图标", systemImage: "heart.fill")
  .labelStyle(.iconOnly)

4Image 与 SF Symbols

Image 常用
何时用

系统符号或资源图。新手优先 SF Symbols,免切图。

初始化(常用)
Image(systemName: "wifi")     // SF Symbol
Image("photo")                // Assets.xcassets 中的名字
Image(decorative: "photo")    // 装饰图(辅助功能可忽略)
Image(uiImage: someUIImage)   // 包一层 UIKit 图
常用修饰符(写全)
修饰符取值 / 说明含义
.resizable(capInsets:resizingMode:)资源图必加,否则按原图像素显示允许被 frame 拉伸/缩放
.scaledToFit()—保持比例完整显示在框内(可能留边)
.scaledToFill()—填满框(可能裁切);常配 clipped()
.imageScale.small / .medium(默认)/ .largeSF Symbol 相对字号缩放
.symbolRenderingMode.monochrome单色(默认观感)
.hierarchical分层深浅
.palette多色,配 foregroundStyle 多层色
.multicolor使用符号自带多色
.foregroundStyle颜色给模板符号上色
.clipShape / .clipShape(Circle())形状裁成圆头像等
示例
HStack(spacing: 16) {
  Image(systemName: "wifi")
    .imageScale(.large)
  Image(systemName: "battery.100")
    .foregroundStyle(.green)
  Image("AppIcon") // Assets 里的名字;示例用系统符号即可
    .resizable()
    .scaledToFit()
    .frame(width: 44, height: 44)
}
资源图忘记 .resizable() 会按原图像素撑爆布局。

5常用交互控件

Button 常用
何时用

点击触发动作。几乎每个界面都有。

初始化(常用写法)
Button("标题", action: { /* 点击 */ })
Button("标题", role: .destructive, action: { })
Button(action: { }, label: { /* 自定义外观 */ })
Button("标题", systemImage: "plus") { }   // 较新系统:标题+SF Symbol
初始化参数
参数类型含义
标题字符串String / LocalizedStringKey按钮上显示的文字
action() -> Void点击时执行的闭包(必写逻辑)
roleButtonRole?语义角色,影响颜色/确认对话框位置,见下表
label@ViewBuilder完全自定义按钮外观(图标+文字等)
role 全部取值
取值含义
nil(不写)普通按钮
.destructive破坏性操作(删除等),通常呈红色
.cancel取消;在确认框里往往被放到特殊位置
常用修饰符(写全常用项)
修饰符常用取值含义
.buttonStyle.automatic跟场景自动选样式(默认)
.borderedProminent实心强调(主按钮)
.bordered描边次要按钮
.borderless无边框
.plain朴素,少视觉重量
.controlSize.mini / .small / .regular / .large / .extraLarge控件尺寸档
.disabledtrue/false禁用点击并变灰
.tintColor着色(影响 Prominent 等)
.keyboardShortcut如 .defaultAction键盘快捷键(Mac/iPad)
示例
VStack(spacing: 12) {
  Button("主操作") { print("go") }
    .buttonStyle(.borderedProminent)
  Button("次要", role: .cancel) { }
    .buttonStyle(.bordered)
  Button("删除", role: .destructive) { }
  Button {
    print("自定义")
  } label: {
    Label("收藏", systemImage: "star")
  }
}
TextField / SecureField 常用
何时用

单行输入;密码用 SecureField。

初始化签名
TextField(_ titleKey: String, text: Binding<String>)
TextField(_ titleKey: String, text: Binding<String>, axis: Axis)  // 可竖向扩展的多行(较新)
SecureField(_ titleKey: String, text: Binding<String>)            // 密码,输入变圆点
参数一览
参数含义
titleKey / 占位文案没输入时的提示文字(并参与无障碍)
textBinding<String>,与 @State 双向绑定,必用 $
axis.horizontal / .vertical;竖向时可多行增高
prompt(部分重载)更明确的占位 View
常用修饰符(写全高频项)
修饰符常用取值含义
.textFieldStyle.automatic / .plain / .roundedBorder外观;圆角边框最直观
.keyboardType.default / .asciiCapable / .numbersAndPunctuation / .URL / .numberPad / .phonePad / .namePhonePad / .emailAddress / .decimalPad / .twitter / .webSearch / .asciiCapableNumberPad弹出哪种键盘
.textInputAutocapitalization.never / .words / .sentences / .characters自动大写策略;邮箱常用 .never
.autocorrectionDisabledtrue关闭自动纠正
.submitLabel.done / .go / .send / .join / .route / .search / .return / .next / .continue键盘回车键显示文案
.onSubmit闭包用户点回车时回调
.focusedFocusState 绑定程序化聚焦/失焦
示例
@State private var email = ""
@State private var pwd = ""
// …
VStack {
  TextField("邮箱", text: $email)
    .keyboardType(.emailAddress)
    .textInputAutocapitalization(.never)
    .textFieldStyle(.roundedBorder)
  SecureField("密码", text: $pwd)
    .textFieldStyle(.roundedBorder)
}
TextEditor 常用
何时用

多行文本编辑(笔记、反馈、长描述)。单行输入用 TextField。

初始化签名
TextEditor(text: Binding<String>)
参数一览(写全)
参数类型默认含义
textBinding<String>(必填)编辑内容的双向绑定,必须用 $(如 $note)

没有 placeholder / 标题参数;占位提示要自己叠一层(见示例)。默认自带系统背景,外观主要靠修饰符。

常用修饰符
修饰符含义
.frame(minHeight:)给最小高度,否则可能塌成一行高
.font / .foregroundStyle改字号、字色
.scrollContentBackground(.hidden)(较新)隐藏默认滚动背景,方便自定义底色
.disabled / .focused禁用、焦点控制
.overlay(RoundedRectangle…stroke)自己画边框(系统 TextEditor 默认不一定有清晰描边)
示例
@State private var note = ""
ZStack(alignment: .topLeading) {
  TextEditor(text: $note)
    .frame(minHeight: 120)
  // 简易占位:内容为空时显示灰字
  if note.isEmpty {
    Text("写点什么…")
      .foregroundStyle(.tertiary)
      .padding(.horizontal, 5)
      .padding(.vertical, 8)
      .allowsHitTesting(false)
  }
}
.overlay(RoundedRectangle(cornerRadius: 8).stroke(.quaternary))
Toggle 常用
何时用

布尔开关。设置页高频。

初始化签名
Toggle(isOn: Binding<Bool>) { /* label */ }
Toggle(_ title: String, isOn: Binding<Bool>)
Toggle(isOn: Binding<Bool>, label: () -> Label)
参数一览
参数类型含义
isOnBinding<Bool>开关状态,必须用 $ 绑定
标题 / label字符串或 View开关左边(或环境决定位置)的说明
常用修饰符
修饰符取值含义
.toggleStyle.automatic默认
.switch经典滑动开关
.button按钮式切换(较新)
.disabledBool禁用
.labelsHidden—隐藏标签(无障碍仍可读)
.tintColor打开时的着色
示例
@State private var notify = true
Toggle("推送通知", isOn: $notify)
Toggle(isOn: $notify) {
  Label("通知", systemImage: "bell")
}
Slider / Stepper 常用
何时用

连续数值用 Slider;离散加减用 Stepper。

Slider 参数
Slider(
  value: Binding<Double>,
  in: ClosedRange<Double> = 0...1,
  step: Double? = nil,
  onEditingChanged: (Bool) -> Void = { _ in }
)
参数含义
value当前数值绑定
in可滑动范围,如 0...1、0...100
step步进;nil 连续滑,写 1 则按 1 跳动
onEditingChanged手指按下/抬起时回调,参数 true=正在拖
label / minimumValueLabel / maximumValueLabel可选:无障碍标签、左右端点视图
Stepper 参数
Stepper(
  "标题",
  value: Binding<Int>,
  in: ClosedRange<Int>,
  step: Int = 1,
  onEditingChanged: (Bool) -> Void = { _ in }
)
参数含义
标题 / label显示文案(可插值显示当前值)
value绑定的整数/浮点
in允许范围,如 1...99
step点一次加/减多少,默认 1
onEditingChanged编辑中状态回调
示例
@State private var volume = 0.5
@State private var qty = 1
Slider(value: $volume, in: 0...1)
Stepper("数量 \(qty)", value: $qty, in: 1...20)
ProgressView 常用
何时用

加载中或进度条。

常用初始化
ProgressView()                      // 转圈,不确定进度
ProgressView("加载中…")             // 转圈 + 文案
ProgressView(value: 0.4)            // 确定进度 0...1
ProgressView(value: 30, total: 100) // 当前/总量
ProgressView(value: 0.4) { Text("下载") } currentValueLabel: { Text("40%") }
参数含义
参数含义
(无 value)不确定进度(转圈)
value当前进度
total总量,默认 1.0;进度 = value/total
label / currentValueLabel主说明、当前值说明
常用修饰符
修饰符取值含义
.progressViewStyle.automatic / .circular / .linear转圈或条状
.tintColor进度条颜色
示例
ProgressView("请稍候…")
ProgressView(value: 0.35)
  .padding(.horizontal)

6Form、Section、Picker

Form + Section 常用
何时用

设置页、资料编辑页的标准容器,系统自动分组样式。

Form

Form { ... } —— 无初始化参数;内部用 Section 分组。系统自动给背景、分隔、边距。

Section 常用初始化
Section { /* rows */ }
Section("标题") { /* rows */ }
Section { /* rows */ } header: { Text("头") } footer: { Text("尾") }
Section(isExpanded: $open) { /* iOS 17+ 可折叠 */ }
参数 / 闭包含义
标题字符串分组标题
header / footer自定义头尾 View
content组内行(Toggle、NavigationLink 等)
isExpanded绑定是否展开(可折叠 Section)
示例
Form {
  Section("账号") {
    TextField("昵称", text: $name)
    Toggle("公开主页", isOn: $isPublic)
  }
  Section("关于") {
    Text("版本 1.0.0")
  }
}
Picker 常用
何时用

从枚举/列表里选一个值。

初始化要点
Picker("标题", selection: $selected) {
  ForEach(options) { opt in
    Text(opt.name).tag(opt)   // tag 的类型必须和 selection 一致
  }
}
参数一览
参数含义
标题无障碍与部分样式下显示的标签
selectionBinding<Value>,当前选中项
content可选项列表;每个选项用 .tag(值) 标记
.pickerStyle 常用取值(写全)
取值外观场景
.automatic随系统默认
.menu弹出菜单选项较多、省地方
.segmented分段控件2~4 个互斥选项
.wheel滚轮日期感、少量枚举
.inline内联展开表单内
.navigationLink推进下一页再选设置页层级选择
.palette(较新)调色板式工具栏等
示例
enum Theme: String, CaseIterable, Identifiable {
  case system, light, dark
  var id: String { rawValue }
}
@State private var theme: Theme = .system
Picker("外观", selection: $theme) {
  ForEach(Theme.allCases) { t in
    Text(t.rawValue).tag(t)
  }
}
.pickerStyle(.segmented)
DatePicker / ColorPicker 偶尔
何时用

选日期/时间用 DatePicker;选颜色用 ColorPicker(主题色、标签色)。

DatePicker 初始化(常用)
DatePicker(
  _ title: String,
  selection: Binding<Date>,
  displayedComponents: DatePickerComponents = [.date, .hourAndMinute]
)
DatePicker(
  _ title: String,
  selection: Binding<Date>,
  in: ClosedRange<Date>,           // 可选:限制可选范围
  displayedComponents: DatePickerComponents
)
DatePicker 参数一览(写全)
参数类型含义
标题字符串 / View表单项标签
selectionBinding<Date>当前选中的日期时间
inPartialRangeFrom / PartialRangeThrough / ClosedRange<Date>可选日期范围,如 ...Date()(不能选未来)、min...max
displayedComponentsDatePickerComponents显示哪些部分,见下表
displayedComponents 全部常用取值
取值含义
.date只选年月日
.hourAndMinute只选时分
[.date, .hourAndMinute]日期 + 时间(默认观感常是二者)
.datePickerStyle 常用取值
取值外观场景
.automatic随系统默认
.compact一行紧凑,点开再选表单里最省地方
.graphical内联月历需要一眼看见整月
.wheel滚轮偏 UIKit 经典感
ColorPicker 参数一览(写全)
ColorPicker(_ title: String, selection: Binding<Color>, supportsOpacity: Bool = true)
ColorPicker(selection: Binding<Color>, supportsOpacity: Bool = true) { /* label */ }
参数类型默认含义
标题 / label字符串或 View—左侧说明
selectionBinding<Color>(必填)当前颜色
supportsOpacityBooltrue是否允许调透明度;只要不透明色时设 false
示例
@State private var birthday = Date()
@State private var accent = Color.blue

DatePicker("生日", selection: $birthday, in: ...Date(), displayedComponents: .date)
  .datePickerStyle(.compact)

DatePicker("开会时间", selection: $meeting, displayedComponents: [.date, .hourAndMinute])

ColorPicker("强调色", selection: $accent, supportsOpacity: false)

7修饰符速查(按频率)

修饰符顺序有意义:先 padding 再 background,背景含内边距;反过来则背景更贴文字。

修饰符频率作用常见写法
padding常用内边距.padding() .padding(.horizontal, 16)
frame常用宽高/对齐.frame(maxWidth: .infinity) .frame(width:44,height:44)
background常用背景.background(.ultraThinMaterial)
foregroundStyle常用前景色/样式.foregroundStyle(.secondary)
clipShape / cornerRadius常用圆角裁剪.clipShape(RoundedRectangle(cornerRadius:12))
overlay常用叠加描边/角标.overlay(RoundedRectangle…stroke)
shadow偶尔阴影.shadow(radius: 4, y: 2)
opacity / hidden常用透明/占位隐藏.opacity(0.5);hidden仍占位
disabled常用禁用交互.disabled(!valid)
ignoresSafeArea偶尔延伸到刘海/底部全屏背景图时用
scaledToFit/Fill常用图片适应配 resizable()
listRowInsets 等偶尔列表行细节见第 07 章
Text("卡片")
  .padding()
  .frame(maxWidth: .infinity, alignment: .leading)
  .background(.blue.opacity(0.12))
  .clipShape(RoundedRectangle(cornerRadius: 12))
  .overlay(
    RoundedRectangle(cornerRadius: 12)
      .stroke(.blue.opacity(0.3))
  )

8形状与颜色(打底常用)

RoundedRectangle / Circle / Capsule 常用
何时用

当背景、头像框、按钮底、描边框。本身是形状,要用 .fill / .stroke 上色,再用 .frame 定大小。

初始化参数(写全)
形状初始化参数含义
RoundedRectangleRoundedRectangle(cornerRadius: CGFloat, style: RoundedCornerStyle = .continuous)cornerRadius:圆角半径(pt)。style:.continuous(iOS 更顺滑,默认观感)/ .circular(更几何)
RoundedRectangle(cornerSize: CGSize, style:)宽高方向用不同圆角半径(少用)
CircleCircle()无参数;在给定 frame 里画内切圆
CapsuleCapsule(style: RoundedCornerStyle = .continuous)两端半圆的跑道形;style 同上。无 cornerRadius(由高度自动决定)
Rectangle / EllipseRectangle() / Ellipse()直角矩形 / 椭圆;无初始化参数
上色与描边(Shape 协议常用,写全)
写法含义
.fill(Color / AnyShapeStyle)填充内部
.stroke(style, lineWidth:)只描边,中间透明
.strokeBorder(style, lineWidth:)描边画在边界内侧(不撑大布局,卡片描边常用)
.fill(...).overlay(shape.stroke(...))既填充又描边
示例
HStack(spacing: 12) {
  Circle().fill(.orange).frame(width: 40, height: 40)

  Capsule()
    .fill(.green.opacity(0.3))
    .frame(width: 80, height: 28)

  RoundedRectangle(cornerRadius: 8, style: .continuous)
    .stroke(.blue, lineWidth: 2)
    .frame(width: 60, height: 40)
}

// 卡片底:填充 + 内描边
RoundedRectangle(cornerRadius: 12)
  .fill(.background)
  .overlay(
    RoundedRectangle(cornerRadius: 12)
      .strokeBorder(.quaternary, lineWidth: 1)
  )
  .frame(height: 100)

9List 速览(详情见第 07 章)

常用 系统列表:滑动删除、分区、与 Navigation 绝配。

struct Row: Identifiable { let id = UUID(); let title: String }
@State private var rows = [Row(title: "A"), Row(title: "B")]

List {
  ForEach(rows) { r in
    Text(r.title)
  }
  .onDelete { rows.remove(atOffsets: $0) }
}
导航 push、Tab、toolbar 放到第 07 章系统讲。

10上手菜谱:直接抄的布局

顶栏标题 + 右侧按钮
HStack {
  VStack(alignment: .leading) {
    Text("今日").font(.largeTitle.bold())
    Text("3 项待办").foregroundStyle(.secondary)
  }
  Spacer()
  Button { } label: {
    Image(systemName: "plus.circle.fill").imageScale(.large)
  }
}
.padding()
设置行(图标 + 标题 + 披露)
HStack(spacing: 12) {
  Image(systemName: "bell.fill")
    .frame(width: 28, height: 28)
    .foregroundStyle(.white)
    .background(.red)
    .clipShape(RoundedRectangle(cornerRadius: 6))
  Text("通知")
  Spacer()
  Image(systemName: "chevron.right")
    .foregroundStyle(.tertiary)
}
.padding(.vertical, 4)
空状态页
ContentUnavailableView(
  "暂无待办",
  systemImage: "checklist",
  description: Text("点右上角添加第一项")
)
// iOS 17+;更早可用 VStack + Image + Text 自制

11拆视图与 Preview

常用 body 超过一屏就拆;Preview 保证改 UI 不用次次 ⌘R。

struct TaskRow: View {
  let title: String
  var done: Bool = false
  var body: some View {
    HStack {
      Image(systemName: done ? "checkmark.circle.fill" : "circle")
      Text(title)
      Spacer()
    }
  }
}

#Preview("一行") {
  TaskRow(title: "写需求", done: true)
    .padding()
}

#Preview("列表") {
  List {
    TaskRow(title: "A")
    TaskRow(title: "B", done: true)
  }
}
子视图用 let / var 收配置;要改父数据再用 Binding(第 05 章)。

12API 索引(按字母,方便检索)

名称频率类别跳转
Button常用控件#
ColorPicker偶尔控件#
DatePicker偶尔控件#
Divider常用布局#
Form / Section常用容器#
HStack常用布局#
Image常用展示#
Label常用展示#
LazyVGrid偶尔布局#
LazyVStack偶尔布局#
List常用列表#
Picker常用控件#
ProgressView常用控件#
ScrollView常用布局#
Slider / Stepper常用控件#
Spacer常用布局#
Text常用展示#
TextField / SecureField常用控件#
TextEditor常用控件#
Toggle常用控件#
VStack常用布局#
ZStack常用布局#
padding / frame / …常用修饰符#

13布局 FAQ

为什么按钮点不到 / 颜色不对?
检查是否被透明 overlay 挡住;Button 是否 .disabled;自定义 label 是否把点击区域做太小——可加 .contentShape(Rectangle())。
HStack 里文字被压扁
给文字 .lineLimit(1) + 需要时可 minimumScaleFactor;或让次要视图 .layoutPriority(-1),主文字优先。
想铺满宽度
.frame(maxWidth: .infinity, alignment: .leading) 加在子视图或背景容器上。
和鸿蒙 / Compose 怎么对应?
VStack≈Column,HStack≈Row,ZStack≈Stack/Box,Spacer≈空白弹性,修饰符≈链式 modifier。细节不同,心智同构。

iOS App 从 0 到 1 · 主讲 SwiftUI / iOS