原创

Swift UI常用基础组件(一)

温馨提示:
本文最后更新于 2026年01月03日,已超过 29 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

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(对齐方式)

ZStackalignment 同时控制 横向 + 纵向

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简介

TextFieldSwiftUI 中用于输入与编辑单行文本的基础组件,常用于:

  • 用户名 / 昵称 / 邮箱输入
  • 搜索框
  • 表单输入
@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. 键盘遮挡

👉 需要配合:

  • ScrollView
  • safeAreaInset
  • 或第三方键盘管理

❌ 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 简介

ImageSwiftUI 中用于显示图片资源的基础视图组件,支持:

  • 本地资源(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 简介

ButtonSwiftUI 中最基础、最重要的交互组件之一,用于:

  • 触发操作(提交 / 跳转 / 删除)
  • 表单提交
  • 功能按钮
  • 图标按钮
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)
正文到此结束
本文目录