Swift UI常用基础组件(三)
1 输入与控制类组件
1.1 Toggle
Toggle 用于表示 开 / 关(二值)状态,常见于设置项、权限控制、功能启停。
1.1.1 Toggle 的基本用法
@State private var isOn = true
Toggle("开启通知", isOn: $isOn)
说明:
isOn必须是Binding<Bool>- 文本自动左对齐,开关位于右侧
1.1.2 Toggle 不带文本标签
Toggle(isOn: $isOn) {
Text("夜间模式")
}
适合自定义 Label 结构。
1.1.3 Toggle 的常见修饰符
Toggle("定位服务", isOn: $location)
.tint(.green)
.disabled(!hasPermission)
常用修饰符说明:
| 修饰符 | 作用 |
|---|---|
.tint() |
修改开关高亮颜色 |
.disabled() |
禁用交互 |
.labelsHidden() |
隐藏文本 |
1.2 Picker
Picker 用于在多个选项中选择一个或多个值,是 SwiftUI 中非常重要的选择型组件。
1.2.1 Picker 的基础结构
@State private var selection = 0
Picker("性别", selection: $selection) {
Text("男").tag(0)
Text("女").tag(1)
}
说明:
tag的类型必须与selection一致- Picker 本身不存储数据
1.2.2 PickerStyle(选择器样式)
Picker("城市", selection: $city) {
ForEach(cities, id: \.self) { city in
Text(city)
}
}
.pickerStyle(.menu)
常见样式:
| 样式 | 使用场景 |
|---|---|
.menu |
下拉菜单 |
.wheel |
滚轮(生日、地区) |
.segmented |
少量选项切换 |
.navigationLink |
跳转选择页 |
1.2.3 Picker 与 Form 的结合
Form {
Picker("语言", selection: $language) {
Text("中文").tag("zh")
Text("English").tag("en")
}
}
Form 会自动渲染为系统设置样式。
1.3 DatePicker
DatePicker 用于 日期 / 时间的选择,广泛用于生日、预约、日程等场景。
1.3.1 DatePicker 的基本用法
@State private var date = Date()
DatePicker("选择日期", selection: $date)
1.3.2 日期组件模式(Displayed Components)
DatePicker(
"时间",
selection: $date,
displayedComponents: [.hourAndMinute]
)
可选值:
.date.hourAndMinute- 二者组合
1.3.3 日期范围限制
DatePicker(
"生日",
selection: $birthday,
in: ...Date()
)
说明:
- 防止选择未来日期
- 常用于生日、历史记录
1.4 Slider
Slider 用于 在连续数值范围内拖动选择,适合音量、亮度、进度等控制。
1.4.1 Slider 的基本用法
@State private var volume: Double = 0.5
Slider(value: $volume)
默认范围:0.0 ~ 1.0
1.4.2 自定义数值范围
Slider(value: $progress, in: 0...100)
1.4.3 Slider 与标签
Slider(
value: $value,
in: 0...10,
step: 1
) {
Text("等级")
} minimumValueLabel: {
Text("0")
} maximumValueLabel: {
Text("10")
}
1.5 Stepper
Stepper 用于 离散数值的递增 / 递减控制,常用于数量、等级、页码。
1.5.1 Stepper 的基础用法
@State private var count = 1
Stepper("数量:\(count)", value: $count)
1.5.2 设置范围与步长
Stepper(
"页数",
value: $page,
in: 1...10,
step: 1
)
1.5.3 自定义 Stepper 内容
Stepper {
Text("等级:\(level)")
} onIncrement: {
level += 1
} onDecrement: {
level -= 1
}
适合需要:
- 自定义逻辑
- 条件限制
- 网络校验
2 导航与页面结构
导航与页面结构组件用于组织页面层级、管理页面跳转、构建整体应用框架。 在 SwiftUI 中,导航是状态驱动的路径管理,而不是命令式 push / pop。
2.1 NavigationStack
NavigationStack 是 SwiftUI 中现代化的导航容器,用于管理页面层级结构(iOS 16+)。
2.1.1 NavigationStack 的基本用法
NavigationStack {
Text("首页")
.navigationTitle("Home")
}
说明:
- 必须作为导航根容器
- 一个页面层级只能存在一个
NavigationStack
2.1.2 NavigationStack 与 NavigationPath
@State private var path = NavigationPath()
NavigationStack(path: $path) {
List {
Button("进入详情页") {
path.append(1)
}
}
.navigationDestination(for: Int.self) { value in
Text("详情页 \(value)")
}
}
说明:
NavigationPath是类型安全的路径栈- 支持程序化导航
2.1.3 navigationDestination 的作用
.navigationDestination(for: String.self) { value in
Text("参数:\(value)")
}
作用:
- 声明目标页面
- 与
path.append()或NavigationLink(value:)对应
2.2 NavigationLink
NavigationLink 用于在导航栈中触发页面跳转,是最常用的导航组件。
2.2.1 NavigationLink 的基础用法(View 形式)
NavigationLink("进入详情") {
DetailView()
}
特点:
- 适合静态结构
- 目标页面明确
2.2.2 NavigationLink(value:)(推荐)
NavigationLink("查看详情", value: 10)
配合:
.navigationDestination(for: Int.self) { id in
DetailView(id: id)
}
优点:
- 解耦跳转与目标视图
- 更符合数据驱动理念
2.2.3 NavigationLink 在 List / Form 中
List {
NavigationLink("设置") {
SettingView()
}
}
系统行为:
- 自动显示箭头
- 整行可点击
2.3 TabView
TabView 用于构建 多 Tab 页面结构,通常作为应用的主入口框架。
2.3.1 TabView 的基础结构
TabView {
HomeView()
.tabItem {
Label("首页", systemImage: "house")
}
ProfileView()
.tabItem {
Label("我的", systemImage: "person")
}
}
说明:
- 每个子视图对应一个 Tab
tabItem必须配置
2.3.2 TabView 与 selection
@State private var selectedTab = 0
TabView(selection: $selectedTab) {
HomeView()
.tag(0)
.tabItem { Text("首页") }
MessageView()
.tag(1)
.tabItem { Text("消息") }
}
用途:
- 程序化切换 Tab
- 保存当前 Tab 状态
2.3.3 TabView 与 NavigationStack 的组合
TabView {
NavigationStack {
HomeView()
}
.tabItem {
Label("首页", systemImage: "house")
}
NavigationStack {
ProfileView()
}
.tabItem {
Label("我的", systemImage: "person")
}
}
说明:
- 每个 Tab 独立维护导航栈
- 推荐的主结构方案
2.4 常见结构组合模式
2.4.1 典型 App 页面结构
TabView
├─ NavigationStack(首页)
│ └─ 列表 → 详情
├─ NavigationStack(消息)
└─ NavigationStack(我的)
2.4.2 常见错误用法
❌ 在子页面再次嵌套 NavigationStack
❌ 在非导航环境使用 NavigationLink
❌ 多个 NavigationStack 作用于同一页面层级
2.5 总结
| 组件 | 作用 |
|---|---|
| NavigationStack | 管理页面层级 |
| NavigationLink | 触发页面跳转 |
| TabView | 构建主框架 |
设计建议:
- 单入口导航 → NavigationStack
- 数据驱动跳转 → NavigationLink(value:)
- 主结构 → TabView + 多 NavigationStack
3 状态与反馈组件
3.1 ProgressView
ProgressView 用于表示 任务进行中或加载状态。
3.1.1 ProgressView 的基本用法(不确定进度)
ProgressView("加载中...")
适用场景:
- 网络请求
- 页面初始化
- 数据计算
3.1.2 确定进度 ProgressView
@State private var progress = 0.3
ProgressView(value: progress)
3.1.3 自定义进度范围
ProgressView(value: progress, total: 100)
3.1.4 ProgressViewStyle
ProgressView("同步中")
.progressViewStyle(.circular)
常见样式:
| 样式 | 场景 |
|---|---|
.circular |
等待 / 加载 |
.linear |
文件下载 / 上传 |
3.2 Alert
Alert 用于重要提示或需要用户确认的中断式反馈。
3.2.1 基础 Alert 用法
@State private var showAlert = false
Button("删除") {
showAlert = true
}
.alert("确认删除?", isPresented: $showAlert) {
Button("确定", role: .destructive) { }
Button("取消", role: .cancel) { }
}
特点:
- 中断当前操作
- 必须用户响应
3.2.2 带消息内容的 Alert
.alert(
"操作失败",
isPresented: $showAlert
) {
Button("确定", role: .cancel) {}
} message: {
Text("请检查网络连接")
}
3.2.3 Alert 的使用规范
- 不用于频繁提示
- 不展示长文本
- 一个页面避免多个 Alert
3.3 ConfirmationDialog
确认对话框,ConfirmationDialog 用于 操作确认或多选决策,是 ActionSheet 的现代替代。
3.3.1 ConfirmationDialog 基本用法
@State private var showDialog = false
Button("更多操作") {
showDialog = true
}
.confirmationDialog(
"请选择操作",
isPresented: $showDialog
) {
Button("删除", role: .destructive) {}
Button("编辑") {}
}
3.3.2 ConfirmationDialog 与 Alert 的区别
| 对比项 | Alert | ConfirmationDialog |
|---|---|---|
| 展示位置 | 中央弹窗 | 底部弹出 |
| 操作数量 | 少 | 多 |
| 使用场景 | 强确认 | 操作选择 |
3.3.3 角色(role)的作用
能够根据当前系统显示对应的提示样式
Button("删除", role: .destructive) {}
系统会自动使用危险提示样式
3.4 Toast / HUD
能够自动消失的弹窗提示,SwiftUI 官方未内置 Toast / HUD,通常需要自定义或使用第三方方案。
3.4.1 Toast / HUD 的适用场景
- 操作成功提示
- 非阻塞式反馈
- 临时状态提醒
3.4.2 简易 Toast 实现思路
ZStack {
ContentView()
if showToast {
Text("保存成功")
.padding()
.background(.black.opacity(0.7))
.foregroundColor(.white)
.cornerRadius(8)
}
}
关键点:
- 覆盖在当前视图之上
- 自动消失
- 不阻断交互
3.4.3 HUD(加载遮罩)示例
if isLoading {
ProgressView("加载中...")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.black.opacity(0.3))
}
常用于:
- 防止重复点击
- 等待关键任务完成
3.5 使用规范与设计建议
3.5.1 反馈组件选择建议
| 场景 | 推荐组件 |
|---|---|
| 等待加载 | ProgressView |
| 严重操作 | Alert |
| 多选操作 | ConfirmationDialog |
| 成功提示 | Toast / HUD |
3.5.2 设计原则
- 不要过度提示
- 优先非阻塞反馈
- 风险操作必须确认
3.6 总结
状态与反馈组件是 用户体验的重要保障:
- ProgressView:让用户“有耐心”
- Alert:让用户“慎重操作”
- ConfirmationDialog:让用户“自主选择”
- Toast / HUD:让用户“被温柔提醒”
4 修饰类组件(Decorators)
修饰类组件用于在不改变原有视图结构的前提下,对视图进行视觉或布局上的增强。
SwiftUI 的设计哲学是:
View 不变,行为与外观通过 Modifier 叠加。
4.1 Background / Overlay
background 与 overlay 用于在视图的 下层 / 上层 添加额外内容,是最常用的装饰类修饰符。
4.1.1 background 的基本用法
Text("Hello")
.padding()
.background(Color.blue)
说明:
- 背景视图位于当前视图下方
- 背景大小默认等于当前视图的尺寸
4.1.2 使用 background 添加复杂背景
Text("标签")
.padding()
.background(
RoundedRectangle(cornerRadius: 8)
.fill(Color.gray.opacity(0.2))
)
适合:
- 胶囊标签
- 卡片背景
- 圆角块
4.1.3 overlay 的基本用法
Text("头像")
.padding()
.overlay(
Circle()
.stroke(Color.blue, lineWidth: 2)
)
说明:
- 覆盖在视图最上层
- 常用于边框、标记、遮罩
4.1.4 background 和 overlay 对比
| 对比项 | background | overlay |
|---|---|---|
| 位置 | 视图下方 | 视图上方 |
| 常见用途 | 背景、填充 | 边框、角标 |
| 影响布局 | 否 | 否 |
4.1.5 多层叠加顺序
Text("多层装饰")
.padding()
.background(Color.yellow)
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(Color.red)
)
修饰顺序:
- 从上到下依次叠加
- 顺序不同,结果可能不同
4.2 SafeAreaInset
safeAreaInset 用于在安全区域边缘插入内容,而不是简单地忽略安全区。
4.2.1 SafeAreaInset 的基本用法
ScrollView {
Text("内容区域")
}
.safeAreaInset(edge: .bottom) {
Text("底部操作栏")
.frame(maxWidth: .infinity)
.padding()
.background(.ultraThinMaterial)
}
说明:
- 插入内容会挤压原有布局
- 不覆盖内容
4.2.2 与 ignoresSafeArea 的区别
| 对比项 | safeAreaInset | ignoresSafeArea |
|---|---|---|
| 是否挤压内容 | 是 | 否 |
| 常见用途 | 操作栏 / 工具条 | 背景铺满 |
| 安全性 | 高 | 需谨慎 |
4.2.3 顶部 SafeAreaInset
.safeAreaInset(edge: .top) {
Text("提示信息")
.padding()
.background(Color.orange)
}
适合:
- 顶部公告
- 网络状态提示
4.2.4 SafeAreaInset 的设计建议
- 用于固定功能区
- 不要插入过高视图
- 内容需明确且简洁
4.3 总结
| 修饰方式 | 核心作用 |
|---|---|
| background | 添加下层背景 |
| overlay | 添加上层覆盖 |
| safeAreaInset | 插入安全区内容 |
使用原则:
- 装饰 ≠ 布局
- 能用修饰符解决的问题,不新增容器
- 优先
safeAreaInset,慎用ignoresSafeArea
5. 动画与过渡(Animation / Transition)
5.1 Animation(动画)
5.1.1 基础动画用法
@State private var isExpanded = false
Rectangle()
.frame(width: isExpanded ? 200 : 100)
.animation(.easeInOut, value: isExpanded)
说明:
value变化 → 动画触发- 只对关联状态生效
5.1.2 withAnimation 显式动画
withAnimation(.spring()) {
isExpanded.toggle()
}
适合:
- 按钮点击
- 手势触发
5.1.3 常见动画类型
| 动画 | 特点 |
|---|---|
.easeIn |
慢入 |
.easeOut |
慢出 |
.easeInOut |
平滑 |
.linear |
匀速 |
.spring() |
弹性 |
5.1.4 动画时长与参数
.animation(.easeInOut(duration: 0.3), value: isExpanded)
.spring(response: 0.4, dampingFraction: 0.7)
5.2 Transition(过渡)
Transition 用于视图插入与移除时的动画效果。
5.2.1 基本 Transition 用法
if isShow {
Text("Hello")
.transition(.opacity)
}
⚠️ 必须配合:
withAnimation {
isShow.toggle()
}
5.2.2 常见 Transition 类型
| 过渡 | 说明 |
|---|---|
.opacity |
渐隐渐现 |
.move(edge:) |
从边缘滑入 |
.scale |
缩放 |
.slide |
系统滑动 |
5.2.3 组合 Transition
.transition(
.move(edge: .bottom)
.combined(with: .opacity)
)
5.2.4 asymmetric(非对称过渡)
.transition(
.asymmetric(
insertion: .move(edge: .bottom),
removal: .opacity
)
)
适合:
- 弹窗
- Toast / HUD
5.3 动画与过渡的区别
| 对比项 | Animation | Transition |
|---|---|---|
| 触发方式 | 状态变化 | 视图出现/消失 |
| 作用对象 | 属性 | View 本身 |
| 是否常驻 | 是 | 否 |
5.4 动画使用规范
- 不为静态内容加动画
- 关键操作使用短动画(≤ 0.3s)
- 避免多动画叠加
6 手势与事件(Gesture)
手势用于捕获用户的直接交互行为,如点击、拖拽、缩放等。
6.1 TapGesture(点击)
6.1.1 基础点击手势
Text("点我")
.onTapGesture {
print("Tapped")
}
6.1.2 多次点击
.onTapGesture(count: 2) {
print("Double Tap")
}
6.2 LongPressGesture(长按)
6.2.1 基础长按
Text("长按")
.onLongPressGesture {
print("Long Press")
}
6.2.2 带状态回调
.onLongPressGesture(
minimumDuration: 1,
pressing: { isPressing in
print(isPressing)
}
) {
print("完成")
}
6.3 DragGesture(拖拽)
6.3.1 基本拖拽
@State private var offset = CGSize.zero
Rectangle()
.offset(offset)
.gesture(
DragGesture()
.onChanged { value in
offset = value.translation
}
.onEnded { _ in
offset = .zero
}
)
6.3.2 与动画结合
.onEnded { _ in
withAnimation {
offset = .zero
}
}
6.4 MagnificationGesture(缩放)
@State private var scale: CGFloat = 1
Image("photo")
.scaleEffect(scale)
.gesture(
MagnificationGesture()
.onChanged { scale = $0 }
)
6.5 RotationGesture(旋转)
@State private var angle: Angle = .zero
Rectangle()
.rotationEffect(angle)
.gesture(
RotationGesture()
.onChanged { angle = $0 }
)
6.6 手势组合
6.6.1 同时识别(Simultaneous)
.simultaneousGesture(
TapGesture()
.onEnded { print("Tap") }
)
6.6.2 优先级(High Priority)
.highPriorityGesture(
DragGesture()
)
6.6.3 排他识别(Exclusive)
.exclusiveGesture(
TapGesture(),
LongPressGesture()
)
6.7 手势使用规范
- 避免与系统手势冲突
- 列表中慎用复杂手势
- 手势应有明确反馈(动画)
6.8 本章小结
| 类型 | 作用 |
|---|---|
| Tap | 点击 |
| LongPress | 长按 |
| Drag | 拖拽 |
| Magnification | 缩放 |
| Rotation | 旋转 |
核心原则:
手势 ≠ 功能
手势是触发方式,不是业务本身
7 状态管理
| 类型 | 用途 |
|---|---|
@State |
本地状态 |
@Binding |
双向绑定 |
@ObservedObject |
外部对象 |
@StateObject |
生命周期绑定 |
@Environment |
环境变量 |
8 最常用组件速查表
| 类型 | 组件 |
|---|---|
| 布局 | HStack / VStack / ZStack |
| 列表 | List / ScrollView |
| 表单 | Form / Section |
| 控制 | Toggle / Picker / Slider |
| 导航 | NavigationStack / TabView |
| 反馈 | ProgressView / Alert |
- 本文标签: swift ui
- 本文链接: https://www.tianyajuanke.top/article/96
- 版权声明: 本文由吴沛芙原创发布,转载请遵循《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权