背景
本文帮助有Swift基础的同学,快速入门SwiftUI,基于cursour整理
主要分为四个部分:
- 关键字
- Modifier
- 布局
- Viewbuilder
Some
some 表示"某个特定的类型,该类型遵循某个协议"。它的特点是:
- 隐藏具体类型:调用者不知道具体是什么类型,只知道它遵循某个协议
- 类型固定:返回的始终是同一个具体类型(编译器知道)
- 类型推断:编译器会自动推断出具体类型
some vs any 核心区别
| 特性 | some | any |
|---|---|---|
| 类型确定 | 编译时确定,固定不变 | 运行时可变 |
| 性能 | 快(静态派发) | 慢(动态派发,有装箱开销) |
| 类型一致性 | 必须始终返回同一类型 | 可以返回不同类型 |
| 引入版本 | Swift 5.1 | Swift 5.6 |
| 使用场景 | 返回类型、属性 | 需要类型灵活性时 |
swift
// some - 固定的具体类型
func makeSomeView() -> some View {
Text("Hello") // 每次调用都返回 Text 类型
}
// any - 可以是任何符合协议的类型
func makeAnyView(condition: Bool) -> any View {
if condition {
return Text("Hello") // 这次返回 Text
} else {
return Image("icon") // 下次可能返回 Image
}
}
关键字
| 属性包装器 | 用途 | 拥有数据 | 数据类型 | 典型场景 |
|---|---|---|---|---|
| @State | 当前View状态处理 | ✅ 是 | 值类型 | 简单的 UI 状态 |
| @Binding | 父子View间状态传递 | ❌ 否 | 任意 | 子视图修改父状态 |
| @StateObject | 当前View引用对象,对象的生命周期在当前View | ✅ 是 | 引用类型 | 视图的 ViewModel |
| @ObservedObject | 父子View间对象状态传递,对象在父View | ❌ 否 | 引用类型 | 传入的对象 |
| @EnvironmentObject | 跨View间状态传递 | ❌ 否 | 引用类型 | 全局共享数据 |
| @Environment | 系统环境 | ❌ 否 | 系统提供 | 系统设置和服务 |
1. @State - 私有状态 用于管理视图内部的简单值类型状态。
swift
struct CounterView: View {
@State private var count = 0
@State private var isOn = false
@State private var name = ""
var body: some View {
VStack {
Text("计数: \(count)")
Button("增加") {
count += 1 // 修改会触发视图刷新
}
Toggle("开关", isOn: $isOn)
TextField("姓名", text: $name)
}
}
}
特点:
- ✅ 用于值类型(Int, String, Bool, struct 等)
- ✅ 视图拥有这个状态
- ✅ 声明为 private
- ✅ SwiftUI 管理其生命周期
- ✅ 修改会自动刷新视图
2. @Binding - 双向绑定
创建对父视图状态的双向绑定。
swift
struct ParentView: View {
@State private var isPresented = false
var body: some View {
VStack {
Button("显示") {
isPresented = true
}
// 传递绑定
ChildView(isPresented: $isPresented)
}
}
}
struct ChildView: View {
@Binding var isPresented: Bool // 绑定到父视图的状态
var body: some View {
Toggle("显示状态", isOn: $isPresented)
// 修改会同步到父视图
}
}
特点:
- ✅ 创建双向连接
- ✅ 子视图可以读写父视图的状态
- ✅ 使用 $ 传递绑定
- ✅ 不拥有数据
3. @StateObject - 引用类型的拥有者
用于创建和拥有 ObservableObject 实例
swift
// 1. 创建可观察对象
class ViewModel: ObservableObject {
@Published var items: [String] = []
@Published var isLoading = false
func loadData() {
isLoading = true
// 加载数据...
items = ["Item 1", "Item 2"]
isLoading = false
}
}
// 2. 在视图中使用
struct ContentView: View {
@StateObject private var viewModel = ViewModel()
var body: some View {
List(viewModel.items, id: \.self) { item in
Text(item)
}
.onAppear {
viewModel.loadData()
}
}
}
3. @ObservedObject - 引用类型的观察者
用于观察已存在的 ObservableObject(不拥有)。
swift
class ViewModel: ObservableObject {
@Published var count = 0
}
struct ParentView: View {
@StateObject private var viewModel = ViewModel() // 拥有
var body: some View {
ChildView(viewModel: viewModel) // 传递
}
}
struct ChildView: View {
@ObservedObject var viewModel: ViewModel // 观察(不拥有)
var body: some View {
VStack {
Text("计数: \(viewModel.count)")
Button("增加") {
viewModel.count += 1
}
}
}
}
特点:
- ✅ 观察从外部传入的对象
- ❌ 不拥有对象
- ⚠️ 视图重建时可能导致对象重新初始化(如果使用不当)
5. @EnvironmentObject - 环境对象 在视图层级中共享对象,无需逐层传递。
swift
class UserSettings: ObservableObject {
@Published var username = "Guest"
@Published var isDarkMode = false
}
@main
struct MyApp: App {
@StateObject private var settings = UserSettings()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(settings) // 注入
}
}
}
struct ContentView: View {
@EnvironmentObject var settings: UserSettings // 自动获取
var body: some View {
VStack {
Text("用户: \(settings.username)")
SettingsView() // 子视图也能访问
}
}
}
struct SettingsView: View {
@EnvironmentObject var settings: UserSettings // 直接访问
var body: some View {
Toggle("深色模式", isOn: $settings.isDarkMode)
}
}
特点:
- ✅ 跨层级共享数据
- ✅ 无需逐层传递
- ⚠️ 如果未注入会崩溃
- ✅ 适合全局状态(用户设置、主题等)
6. @Environment - 系统环境值
访问 SwiftUI 提供的系统环境值。
swift
struct MyView: View {
@Environment(\.colorScheme) var colorScheme // 深色/浅色模式
@Environment(\.dismiss) var dismiss // 关闭动作
@Environment(\.horizontalSizeClass) var sizeClass // 尺寸类别
var body: some View {
VStack {
Text("当前模式: \(colorScheme == .dark ? "深色" : "浅色")")
Button("关闭") {
dismiss()
}
}
}
}
常用环境值:
- .colorScheme - 颜色方案
- .dismiss - 关闭当前视图
- .horizontalSizeClass / .verticalSizeClass - 尺寸类别
- .locale - 本地化
- .accessibilityEnabled - 辅助功能
最佳实践
swift
// 1. 简单值用 @State
@State private var count = 0
// 2. 创建对象用 @StateObject
@StateObject private var viewModel = ViewModel()
// 3. 传递对象用 @ObservedObject
@ObservedObject var viewModel: ViewModel
// 4. 传递绑定用 @Binding
@Binding var isPresented: Bool
// 5. 全局共享用 @EnvironmentObject
@EnvironmentObject var settings: AppSettings