Swift UI常用基础组件(一)
1 Stack相关
Stack 本身不绘制内容,只负责布局。
1.1 VStack
垂直(Vertical,上 → 下),和flutter的column效果一样,子元素竖向排列 .
1.1.1 示例
VStack {
Text("Title")
Text("Subtitle")
Button("Confirm") { }
}
1.1.2 基本语法
VStack(alignment: HorizontalAlignment = .center,
spacing: CGFloat? = nil) {
// 子视图
}
| 参数 | 作用 |
|---|---|
alignment |
水平方向对齐方式 |
spacing |
子视图之间的垂直间距 |
content |
子视图(ViewBuilder) |
1.1.3. alignment(对齐方式)
VStack 是「纵向排列」,但 alignment 控制的是 横向对齐
VStack(alignment: .leading) {
Text("Left")
Text("Aligned")
}
| 值 | 含义 |
|---|---|
.leading |
左对齐 |
.center |
居中(默认) |
.trailing |
右对齐 |
1.1.4. spacing(间距)
VStack(spacing: 20) {
Text("A")
Text("B")
Text("C")
}
单位是 points
nil表示使用系统默认间距(自适应)
1.1.5. VStack 与 frame 的关系
VStack 默认尺寸是:
刚好包裹子视图的最小尺寸
VStack {
Text("Hello")
}
.background(Color.red)
如果需要撑满屏幕,需要使用.frame 配置宽高
VStack {
Text("Hello")
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
1.1.6. VStack + Spacer(常用布局技巧)
VStack {
Text("Top")
Spacer()
Text("Bottom")
}
Top固定在顶部Bottom固定在底部Spacer 自动占据中间空间
1.1.7. 常见使用场景
页面整体纵向布局
表单 / 设置页
登录页(标题 → 输入框 → 按钮)
卡片内容排列
1.2 HStack
HStack 是 SwiftUI 中的横向布局容器,用于将多个子视图从左到右依次排列,和flutter的row效果一样,子元素横向排列
1.2.1. 基本语法
HStack(alignment: VerticalAlignment = .center,
spacing: CGFloat? = nil) {
// 子视图
}
| 参数 | 作用 |
|---|---|
alignment |
垂直方向对齐方式 |
spacing |
子视图之间的水平间距 |
content |
子视图(ViewBuilder) |
1.2.2. alignment(对齐方式)
HStack 是横向排列,但 alignment 控制的是 纵向对齐
HStack(alignment: .top) {
Text("Top")
Text("Aligned\nText")
}
alignment参数含义:
| 值 | 含义 |
|---|---|
.top |
顶部对齐 |
.center**** |
居中(默认) |
.bottom |
底部对齐 |
.firstTextBaseline |
文本首行基线 |
.lastTextBaseline |
文本末行基线 |
1.2.3. HStack 的典型使用场景
导航栏(标题 + 操作按钮)
列表行(左图标 + 文本 + 右箭头)
表单一行多元素
工具栏 / 操作栏
1.2.4. 常见坑点总结
❌ 误以为 alignment 控制左右
✅ 实际控制的是 上下对齐
❌ 想用 HStack 实现换行
✅ SwiftUI 中 HStack 不会自动换行(需用 LazyVGrid)
1.2.5. 备注
其他的属性和VStack类似,只是一个是横向一个是纵向
1.3 ZStack
1.3.1. 基本概念
ZStack 是 SwiftUI 中用于层叠视图的布局容器,类似于flutter中的Stack组件
- 子视图沿 Z 轴叠放
- 后写的视图在上层
- 常用于:背景、遮罩、浮层、角标、弹窗
1.3.2. 基本语法
ZStack(alignment: Alignment = .center) {
// 子视图
}
参数说明
| 参数 | 作用 |
|---|---|
alignment |
子视图在 ZStack 中的对齐方式 |
content |
子视图 |
1.3.3. 图层顺序(非常重要)
ZStack {
Text("Bottom")
Text("Top")
}
规则:
- 越靠后的视图,层级越高
- 写在下面的,会盖住前面的
📌 记忆法:代码顺序 = 图层顺序
1.3.4. alignment(对齐方式)
ZStack 的 alignment 同时控制 横向 + 纵向
ZStack(alignment: .bottomTrailing) {
Color.gray
Text("Corner")
}
常用对齐值:
| 对齐方式 |
|---|
.center(默认) |
.top / .bottom |
.leading / .trailing |
.topLeading |
.topTrailing |
.bottomLeading |
.bottomTrailing |
1.3.5. ZStack 作为背景容器(最常见)
ZStack {
RoundedRectangle(cornerRadius: 12)
.fill(Color.blue)
VStack {
Text("Title")
Text("Description")
}
}
📌 ZStack ≈ background + content
1.3.6. ZStack 和 .background()/.overlay()`
等价写法对比
Text("Hello")
.background(Color.yellow)
等价于:
ZStack {
Color.yellow
Text("Hello")
}
overlay 示例
Image(systemName: "bell")
.overlay(
Circle()
.fill(Color.red)
.frame(width: 10, height: 10)
.offset(x: 6, y: -6)
)
📌 .background / .overlay 本质都是 ZStack 的语法糖
1.3.7. ZStack + Spacer(边角定位技巧)
ZStack {
Color.black
VStack {
HStack {
Spacer()
Text("Top Right")
}
Spacer()
}
}
👉 用 VStack + HStack + Spacer 实现复杂定位
1.3.8. ZStack 的尺寸规则
默认:
ZStack 的尺寸 = 最大子视图尺寸
ZStack {
Text("Small")
Rectangle()
.frame(width: 200, height: 200)
}
👉 ZStack 会被 Rectangle 撑大
1.3.9. 常见使用场景
- 页面背景
- 卡片背景
- 图片 + 文本遮罩
- 红点角标
- 弹窗 / Loading 遮罩
- 半透明蒙层
1.2 ScrollView + Stack
使用频率最高的组合,Stack负责布局,ScrollView 负责滚动
1.2.1 最简单测试
ScrollView(.vertical, showsIndicators: true) {
// 只能有一个直接子视图
}
方向参数:
| 值 |
|---|
.vertical(默认) |
.horizontal |
1.2.2 常见使用组合
1. 纵向滚动 ScrollView + VStack
ScrollView {
VStack(spacing: 16) {
ForEach(0..<20) { i in
Text("Item \(i)")
.frame(maxWidth: .infinity)
.padding()
.background(Color.gray.opacity(0.2))
.cornerRadius(8)
}
}
.padding()
}
📌 适合:
- 列表
- 表单
- 设置页
- 内容流
2. 横向滚动 ScrollView + HStack
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 12) {
ForEach(0..<10) { i in
RoundedRectangle(cornerRadius: 12)
.frame(width: 120, height: 160)
.overlay(Text("Card \(i)"))
}
}
.padding(.horizontal)
}
📌 常见于:
- 卡片流
- Banner
- 图片预览
3. 背景/遮罩类 ScrollView + ZStack
ZStack {
Color.black.opacity(0.05).ignoresSafeArea()
ScrollView {
VStack(spacing: 24) {
ForEach(1...10, id: \.self) { i in
Text("Section \(i)")
.font(.title)
}
}
.padding()
}
}
📌 用途:
- 固定背景
- 半透明遮罩
- 浮层滚动内容
1.2.3 注意事项
1. ScrollView 只能有一个子视图
2. ScrollView 尺寸陷阱(非常重要)
❌ ScrollView 中使用 .frame(maxHeight: .infinity)
ScrollView {
VStack {
Text("Hello")
}
.frame(maxHeight: .infinity) // ❌
}
👉 会导致:
- 滚动失效
- 内容被拉伸
📌 原因:
ScrollView 需要根据 内容高度 来计算滚动范围
2. 文本相关
2.1 Text
2.1.1. 基础使用
Text("Hello").font(.system(size:45)) // 会去 Localizable.strings 查找
最常见用法
自动支持 本地化 Key(⚠️ 容易忽略)
2.1.2. 使用string
纯字符串,不会走本地化key
let name = "Swift"
Text(name)
2.1.3. 使用 LocalizedStringKey 多语言项目常用
Text(LocalizedStringKey("home_title"))
- 明确声明这是本地化文本
- 推荐在多语言项目中使用
2.1.4. 使用格式化文本(iOS 15+)
Text("Score: \(score)")
或
Text(score, format: .number)
Text(date, format: .dateTime.year().month().day())
2.2 Text 常用修饰符
2.2.1. 字体相关
Text("Title")
.font(.title)
常用系统字体:
| 字体 | 说明 |
|---|---|
.largeTitle |
大标题 |
.title / .title2 / .title3 |
标题 |
.headline |
标题级正文 |
.body |
正文 |
.callout |
次要文本 |
.subheadline |
子标题 |
.footnote |
脚注 |
.caption / .caption2 |
说明性文字 |
2.2.2. 自定义字体
Text("Custom Font")
.font(.system(size: 18, weight: .bold, design: .rounded))
参数含义:
| 参数 | 说明 |
|---|---|
size |
字号 |
weight |
字重(light / regular / bold 等) |
design |
字体风格(default / rounded / monospaced) |
2.2.3. 颜色与样式
foregroundColor(_:)
Text("Warning")
.foregroundColor(.red)
foregroundStyle(_:) (iOS 15+)
Text("Gradient")
.foregroundStyle(.linearGradient(
colors: [.purple, .blue],
startPoint: .leading,
endPoint: .trailing
))
2.2.4.对齐与布局
multilineTextAlignment(_:)
Text("多行文本示例")
.multilineTextAlignment(.center)
参数说明
| 值 | 说明 |
|---|---|
.leading |
左对齐 |
.center |
居中 |
.trailing |
右对齐 |
lineLimit(_:) 用于字数超限后省略
Text("超长文本")
.lineLimit(2)
nil:不限制1:单行(常配合省略)
truncationMode(_:)
Text("This is a long text")
.lineLimit(1)
.truncationMode(.tail)
| 值 | 说明 |
|---|---|
.head |
开头省略 |
.middle |
中间省略 |
.tail |
结尾省略(默认) |
2.2.5.行间距与字距
lineSpacing(_:)
Text("多行文本")
.lineSpacing(8)
kerning(_:)
Text("Letter Spacing")
.kerning(2)
2.2.6. 文本装饰
加粗 / 斜体
Text("Bold").bold()
Text("Italic").italic()
下划线 / 删除线
Text("Underline")
.underline(true, color: .blue)
Text("Strikethrough")
.strikethrough(true, color: .red)
2.2.7. 布局影响相关
minimumScaleFactor(_:)
Text("很长的文本")
.lineLimit(1)
.minimumScaleFactor(0.5)
- 字体最小缩放比例
- 常用于自适应按钮标题
allowsTightening(_:)
Text("Tightening")
.allowsTightening(true)
- 允许系统压缩字符间距
2.3 富文本与组合文本
2.3.1. 文本拼接(推荐方式)
Text("Hello ")
+ Text("Swift").bold().foregroundColor(.orange)
SwiftUI 会自动合并为一个 Text View
2.3.2. 使用 AttributedString(iOS 15+)
var attr = AttributedString("Hello SwiftUI")
attr.font = .system(size: 18)
attr.foregroundColor = .blue
Text(attr)
支持:
- 不同字体
- 不同颜色
- 下划线 / 链接
2.3.3.Text 与可访问性(Accessibility)
Text("⚠️")
.accessibilityLabel("Warning")
常用修饰符:
| 修饰符 | 作用 |
|---|---|
accessibilityLabel |
朗读内容 |
accessibilityHidden |
是否隐藏 |
accessibilityHint |
提示说明 |
2.3.4 其他
1. 常见使用组合
Text("欢迎使用 SwiftUI")
.font(.title2)
.fontWeight(.semibold)
.foregroundColor(.primary)
.multilineTextAlignment(.center)
.lineLimit(2)
.padding()
2. 常见误区与注意点
❌ 误区 1:Text 可编辑
不能,要用 TextField
❌ 误区 2:Text("xxx") 不会本地化
→ 会自动查找 Localizable.strings
❌ 误区 3:字体大小写死
→ 推荐使用 .font(.body),支持动态字体
2.4 Textfield
2.4.1. Textfield简介
TextField 是 SwiftUI 中用于输入与编辑单行文本的基础组件,常用于:
- 用户名 / 昵称 / 邮箱输入
- 搜索框
- 表单输入
@State private var username = ""
TextField("请输入用户名", text: $username)
⚠️ 只能输入单行文本
多行输入请使用TextEditor
2.4.2. 构造方式
1️⃣最常用构造器
TextField(_ titleKey: LocalizedStringKey, text: Binding<String>)
示例:
@State private var name = ""
TextField("姓名", text: $name)
参数含义
| 参数 | 说明 |
|---|---|
titleKey |
占位符(placeholder),支持本地化 |
text |
绑定的输入内容(双向绑定) |
2️⃣使用 String 作为占位符
TextField(String("Email"), text: $email)
- 不会自动本地化
3️⃣ 数值类型绑定(iOS 15+)
@State private var age: Int = 18
TextField("年龄", value: $age, format: .number)
构造器:
TextField(_ titleKey: LocalizedStringKey,
value: Binding<T>,
format: FormatStyle)
适用于:
- Int / Double / Date
4️⃣ 自定义 Label(iOS 15+)
TextField(text: $name) {
Text("用户名")
}
2.4.3. TextField 常用修饰符
1⌨️ 键盘与输入行为
keyboardType(_:)
TextField("邮箱", text: $email)
.keyboardType(.emailAddress)
常见值:
| 类型 | 用途 |
|---|---|
.default |
默认 |
.numberPad |
数字 |
.decimalPad |
小数 |
.phonePad |
电话 |
.emailAddress |
邮箱 |
.URL |
URL |
textInputAutocapitalization(_:)(iOS 15+)
.textInputAutocapitalization(.none)
| 值 | 说明 |
|---|---|
.never / .none |
不自动大写 |
.words |
单词首字母 |
.sentences |
句首 |
.characters |
全大写 |
autocorrectionDisabled(_:)
.autocorrectionDisabled(true)
- 禁用系统自动纠错(用户名/账号必加)
2🔒 安全与隐私
secureField
SecureField("密码", text: $password)
SecureField是 TextField 的安全变体
3🎨 外观与样式
textFieldStyle(_:)
TextField("搜索", text: $query)
.textFieldStyle(.roundedBorder)
系统样式:
| 样式 | iOS |
|---|---|
.automatic |
默认 |
.plain |
无边框 |
.roundedBorder |
圆角边框 |
.squareBorder |
macOS |
foregroundColor(_:)
.foregroundColor(.primary)
font(_:)
.font(.body)
4📐 输入限制与行为控制
onChange(of:)
.onChange(of: text) { newValue in
if newValue.count > 10 {
text = String(newValue.prefix(10))
}
}
👉 最常见:限制输入长度
submitLabel(_:)
.submitLabel(.done)
控制键盘 Return 键:
| 值 |
|---|
.done |
.next |
.search |
.go |
.send |
onSubmit
.onSubmit {
print("提交内容")
}
5🧠 焦点管理(iOS 15+)
@FocusState
@FocusState private var isFocused: Bool
TextField("用户名", text: $name)
.focused($isFocused)
控制焦点:
isFocused = true
6🧩 禁用 / 只读
.disabled(true)
SwiftUI 暂无 readonly,只能禁用
2.4.5 TextField 在表单中的典型用法
Form {
TextField("用户名", text: $username)
SecureField("密码", text: $password)
}
2.4.6 其他
1. 常见坑点(⚠️ 很重要)
❌ 1. 不绑定 @State
TextField("xx", text: .constant(""))
→ 无法输入
❌ 2. 键盘遮挡
👉 需要配合:
ScrollViewsafeAreaInset- 或第三方键盘管理
❌ 3. 输入法联想截断问题
限制长度时要注意 中文输入法高亮阶段
正确方式:
.onChange(of: text) { newValue in
guard newValue.count <= 10 else { return }
}
2. 完整示例(推荐规范写法)
@State private var email = ""
TextField("请输入邮箱", text: $email)
.keyboardType(.emailAddress)
.textInputAutocapitalization(.none)
.autocorrectionDisabled(true)
.submitLabel(.done)
.textFieldStyle(.roundedBorder)
.padding()
3 Image
3.1 简介
Image 是 SwiftUI 中用于显示图片资源的基础视图组件,支持:
- 本地资源(Assets / Bundle)
- SF Symbols(系统图标)
- 网络图片(需配合第三方或自定义)
- 位图 / 矢量图(PDF)
Image("avatar")
⚠️
Image只负责“展示”,不负责加载网络资源
3.2 Image 的构造方式(Initializer)
3.2.1. 从 Assets 加载图片(最常用)
Image("logo")
- 自动从 Assets.xcassets 查找
- 支持暗黑模式 / 多分辨率
- 支持 PDF 矢量图
3.2.2. 指定 Bundle
Image("icon", bundle: .main)
常用于:
- Swift Package
- 组件化 / 模块化项目
3.2.3. 使用 SF Symbols(系统图标)
Image(systemName: "star.fill")
特点:
- 矢量图
- 可调颜色、大小、粗细
- 强烈推荐用于功能性图标
3.2.4. 使用 UIImage / NSImage
Image(uiImage: UIImage(named: "avatar")!)
适用于:
- UIKit / SwiftUI 混用
- 三方 SDK 返回 UIImage
3.3 Image 的核心修饰符
3.3.1. 📐尺寸与缩放(最重要)
resizable()
Image("banner")
.resizable()
❗ 不加 resizable,frame 不会生效
scaledToFit()
.resizable()
.scaledToFit()
- 等比缩放
- 保证完整显示
- 可能留白
scaledToFill()
.resizable()
.scaledToFill()
- 等比铺满
- 可能裁剪
aspectRatio(_:contentMode:)
.aspectRatio(16/9, contentMode: .fit)
frame(width:height:)
.frame(width: 100, height: 100)
📌 推荐组合:
Image("avatar")
.resizable()
.scaledToFill()
.frame(width: 80, height: 80)
.clipped()
3.3.2 ✂️ 裁剪与形状
clipped()
.clipped()
clipShape(_:)
.clipShape(Circle())
常见形状:
| 形状 | 用途 |
|---|---|
Circle() |
头像 |
RoundedRectangle |
卡片 |
Capsule() |
标签 |
3.3.3. 🎨 颜色与渲染模式
renderingMode(_:)
.renderingMode(.template)
| 模式 | 说明 |
|---|---|
.original |
原图颜色 |
.template |
作为模板(可染色) |
foregroundColor(_:)
Image(systemName: "heart.fill")
.foregroundColor(.red)
⚠️ 仅对 template 模式 / SF Symbols 有效
3.3.4. 🧱 对齐与布局
alignment
.frame(width: 100, height: 100, alignment: .top)
3.3.5. 🧠 可访问性(Accessibility)
Image("warning")
.accessibilityLabel("警告图标")
或隐藏:
.accessibilityHidden(true)
3.4 Image 与网络图片
SwiftUI 原生不支持网络图片,常见方案:
3.4.1. AsyncImage(iOS 15+)
AsyncImage(url: URL(string: imageUrl)) { image in
image.resizable()
} placeholder: {
ProgressView()
}
优点:
- 官方支持
- 简单
缺点:
- 无缓存控制
- 功能有限
3.4.2. 第三方库(推荐)
| 库 | 特点 |
|---|---|
| Kingfisher | 功能最全 |
| SDWebImageSwiftUI | 成熟稳定 |
| NukeUI | 现代、轻量 |
3.4.3 其他
1. 常见误区(⚠️高频)
❌ 1. frame 不生效
👉 忘记 .resizable()
❌ 2. 图片拉伸变形
👉 使用 scaledToFit / scaledToFill
❌ 3. foregroundColor 无效
👉 图片不是 template 模式
❌ 4. 大图内存暴涨
👉 Assets 勾选 Preserve Vector Data
2. 综合示例(头像组件)
Image("avatar")
.resizable()
.scaledToFill()
.frame(width: 60, height: 60)
.clipShape(Circle())
.overlay(
Circle().stroke(Color.white, lineWidth: 2)
)
4. Button
4.1 简介
Button 是 SwiftUI 中最基础、最重要的交互组件之一,用于:
- 触发操作(提交 / 跳转 / 删除)
- 表单提交
- 功能按钮
- 图标按钮
Button("提交") {
print("点击了按钮")
}
Button = 交互行为 + 视图表现
4.2 Button 的构造方式
4.2.1 最常用构造器(标题按钮)
Button(_ titleKey: LocalizedStringKey, action: () -> Void)
示例:
Button("登录") {
login()
}
参数含义
| 参数 | 说明 |
|---|---|
titleKey |
按钮标题(支持本地化) |
action |
点击触发的闭包 |
4.2.2 自定义内容(推荐)
Button {
submit()
} label: {
HStack {
Image(systemName: "paperplane.fill")
Text("发送")
}
}
⚠️ 实际项目 90% 都用这种
4.2.3 角色 Button(iOS 15+)
Button("删除", role: .destructive) {
deleteItem()
}
| role | 行为 |
|---|---|
.destructive |
危险操作(红色) |
.cancel |
4.3 Button 的核心修饰符
4.3.1 外观与样式
buttonStyle(_:)
Button("确认") {}
.buttonStyle(.bordered)
系统样式(iOS)
| 样式 | 说明 |
|---|---|
.automatic |
默认 |
.plain |
无样式 |
.bordered |
边框 |
.borderedProminent |
主按钮 |
.link |
链接样式 |
tint(_:)(iOS 15+)
.tint(.blue)
控制背景色 / 强调色
4.3.2 尺寸与布局
.frame(height: 44)
.padding(.horizontal)
🔒 启用 / 禁用
.disabled(isLoading)
- 自动降低透明度
- 禁用点击
4.3.3 点击反馈
controlSize(_:)
.controlSize(.large)
| 值 |
|---|
.mini |
.small |
.regular |
.large |
4.3.4 键盘提交(表单场景)
.submitLabel(.done)
配合 onSubmit
4.4 Button Label 的常见写法
4.4.1 纯文本按钮
Button("确定") {}
4.4.2 图标按钮(SF Symbols)
Button {
like()
} label: {
Image(systemName: "heart.fill")
}
4.4.3 图文按钮(最常见)
Button {
buy()
} label: {
HStack(spacing: 8) {
Image(systemName: "cart.fill")
Text("加入购物车")
}
}
4.4.4 自定义背景按钮
Button {
action()
} label: {
Text("保存")
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(Color.blue)
.cornerRadius(10)
}
⚠️ 使用
.buttonStyle(.plain)防止系统干扰
4.5 ButtonStyle
4.5.1自定义 ButtonStyle
struct ScaleButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.scaleEffect(configuration.isPressed ? 0.95 : 1)
.opacity(configuration.isPressed ? 0.7 : 1)
}
}
使用:
Button("点击") {}
.buttonStyle(ScaleButtonStyle())
4.6 Button 与导航 / 弹窗
4.6.1 跳转(NavigationStack)
NavigationLink("详情") {
DetailView()
}
NavigationLink 本质是 Button
4.6.2 弹窗确认
Button("删除") {
showAlert = true
}
.alert("确认删除?", isPresented: $showAlert) {
Button("删除", role: .destructive) {}
Button("取消", role: .cancel) {}
}
4.7 其他
4.7.1 Button 常见坑
❌ 1. Button 内嵌 Button
Button {
} label: {
Button("错误") {}
}
👉 无效,事件冲突
❌ 2. 点击区域太小
👉 用 .contentShape(Rectangle())
.contentShape(Rectangle())
❌ 3. 手动加点击手势
.onTapGesture { }
👉 破坏 Button 的可访问性
❌ 4. 自定义样式被系统覆盖
👉 使用:
.buttonStyle(.plain)
4.7.2 Button vs onTapGesture
| Button | onTapGesture |
|---|---|
| 有可访问性 | 无 |
| 有焦点 | 无 |
| 有禁用态 | 无 |
| 推荐 | ❌ |
4.7.3 完整示例
Button {
submit()
} label: {
Text("提交")
.font(.headline)
.foregroundColor(.white)
.frame(maxWidth: .infinity, height: 48)
.background(Color.blue)
.cornerRadius(12)
}
.buttonStyle(.plain)
.disabled(isSubmitting)
- 本文标签: swift ui
- 本文链接: https://www.tianyajuanke.top/article/94
- 版权声明: 本文由吴沛芙原创发布,转载请遵循《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权