Chapter 02

SwiftUI 基础:View 与修饰符

理解声明式 UI 编程思想,掌握 View 协议与修饰符链的核心机制

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 中,一切皆 ViewView 是一个协议,要求实现一个计算属性 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)以及修饰符链的使用。关键要记住:修饰符顺序影响结果,视图组合优于单一大视图