2.1 声明式 UI vs 命令式 UI
传统的 UIKit 是命令式编程:你告诉系统"怎么做"——先创建视图,设置属性,添加到父视图,注册回调……每一步都需要明确指令。
SwiftUI 是声明式编程:你只需描述"界面应该是什么样子",框架负责计算差异并更新 UI。当数据变化时,SwiftUI 自动重新渲染相关视图。
UIKit(命令式)
let label = UILabel()
label.text = "Hello"
label.font = .systemFont(ofSize: 24)
label.textColor = .blue
label.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
// ...添加约束...
SwiftUI(声明式)
Text("Hello")
.font(.system(size: 24))
.foregroundColor(.blue)
// 就这些!
2.2 View 协议
在 SwiftUI 中,一切皆 View。View 是一个协议,要求实现一个计算属性 body,返回视图的内容描述。
import SwiftUI
struct ContentView: View {
var body: some View {
Text("Hello, SwiftUI!")
}
}
some View 是什么?
some View 是"不透明类型"(Opaque Type),表示"某个具体的 View 类型,但调用者不需要知道是哪种"。这让 SwiftUI 能在编译时确定视图类型,提升性能。
2.3 基础视图组件
Text — 文本
Text("普通文本")
Text("**粗体** 和 *斜体*") // Markdown 支持
Text(Date(), style: .time) // 动态时间显示
Text("很长的文本...")
.lineLimit(2) // 最多2行
.truncationMode(.middle) // 中间截断
Image — 图片
Image("photo") // Assets 中的图片
.resizable()
.scaledToFit()
.frame(width: 200)
Image(systemName: "heart.fill") // SF Symbols 图标
.font(.system(size: 30))
.foregroundColor(.red)
Button — 按钮
Button("点我") {
print("被点击了")
}
Button {
// 操作
} label: {
HStack {
Image(systemName: "plus")
Text("添加")
}
}
TextField — 输入框
@State private var inputText = ""
TextField("请输入...", text: $inputText)
.textFieldStyle(.roundedBorder)
.padding()
2.4 修饰符(Modifiers)
修饰符是 SwiftUI 的核心机制之一。每个修饰符接收一个视图,返回一个新的视图(装饰器模式)。多个修饰符可以链式调用,顺序非常重要。
Text("标题")
.font(.largeTitle) // 字体大小
.fontWeight(.bold) // 字重
.foregroundColor(.white) // 文字颜色
.padding() // 内边距(所有方向)
.padding(.horizontal, 20) // 水平方向额外内边距
.background(.blue) // 背景色
.cornerRadius(12) // 圆角
.shadow(radius: 4) // 阴影
修饰符顺序影响结果
.padding().background(.blue) 与 .background(.blue).padding() 结果不同:前者蓝色背景包含 padding,后者 padding 在蓝色背景外侧。
常用修饰符一览
| 类别 | 修饰符 | 说明 |
|---|---|---|
| 布局 | .frame(width:height:) | 设置尺寸 |
.padding() | 内边距 | |
.offset(x:y:) | 偏移位置 | |
.position(x:y:) | 绝对定位 | |
| 外观 | .background() | 背景 |
.foregroundColor() | 前景色(文字/图标) | |
.opacity() | 透明度 0.0~1.0 | |
| 文字 | .font() | 字体 |
.multilineTextAlignment() | 多行对齐方式 | |
| 交互 | .onTapGesture {} | 点击手势 |
.disabled() | 禁用交互 |
2.5 组合视图
SwiftUI 鼓励将大视图拆分成小的、可复用的子视图。提取子视图不仅提升可读性,还能加速编译。
// ❌ 臃肿的单一视图
struct ContentView: View {
var body: some View {
VStack {
HStack {
Image(systemName: "person")
VStack(alignment: .leading) {
Text("小明").font(.headline)
Text("iOS 开发者").font(.caption)
}
}
// ...更多内容
}
}
}
// ✅ 提取为子视图
struct UserCard: View {
let name: String
let role: String
var body: some View {
HStack {
Image(systemName: "person.circle.fill")
.font(.system(size: 40))
.foregroundColor(.blue)
VStack(alignment: .leading) {
Text(name).font(.headline)
Text(role).font(.caption).foregroundColor(.secondary)
}
}
.padding()
.background(.regularMaterial)
.cornerRadius(12)
}
}
struct ContentView: View {
var body: some View {
VStack {
UserCard(name: "小明", role: "iOS 开发者")
UserCard(name: "小红", role: "设计师")
}
}
}
2.6 ViewBuilder 与条件视图
SwiftUI 使用 @ViewBuilder 属性包装器支持在 body 中使用条件判断和循环,这些都会被编译成视图层级。
struct StatusView: View {
let isOnline: Bool
var body: some View {
HStack {
Circle()
.fill(isOnline ? Color.green : .gray)
.frame(width: 10, height: 10)
Text(isOnline ? "在线" : "离线")
.foregroundColor(isOnline ? .green : .secondary)
// if-else 也可以
if isOnline {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.green)
}
}
}
}
2.7 预览(Preview)
Xcode 提供实时预览功能,让你无需编译运行就能看到界面效果。
#Preview {
ContentView()
}
// 预览多种状态
#Preview("在线状态") {
StatusView(isOnline: true)
}
#Preview("离线状态") {
StatusView(isOnline: false)
}
小结
本章学习了声明式 UI 的核心思想、View 协议、基础视图组件(Text/Image/Button)以及修饰符链的使用。关键要记住:修饰符顺序影响结果,视图组合优于单一大视图。