SwiftUI快速入门指南-关键字篇

背景

本文帮助有Swift基础的同学,快速入门SwiftUI,基于cursour整理

主要分为四个部分:

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
相关推荐
xiAo_Ju2 小时前
SwiftUI快速入门指南-Modifier篇
swiftui
东坡肘子14 小时前
毕业 30 年同学群:一场 AI 引发的“真假难辨”危机 -- 肘子的 Swift 周报 #112
人工智能·swiftui·swift
非专业程序员5 天前
Swift 多线程读变量安全吗?
swiftui·swift
RickeyBoy5 天前
Swift6 @retroactive:Swift 的重复协议遵循陷阱
swiftui·swift
东坡肘子8 天前
Homebrew 5.0:并行加速、MCP 加持,与 Intel 的最后倒计时 -- 肘子的 Swift 周报 #0111
rust·swiftui·swift
RickeyBoy8 天前
Swift 6 迁移常见 crash: _dispatch_assert_queue_fail
swiftui·swift
非专业程序员9 天前
精读GitHub - swift-markdown-ui
ios·swiftui·swift
Daniel_Coder13 天前
iOS Widget 开发-9:可配置 Widget:使用 IntentConfiguration 实现参数选择
ios·swiftui·swift·widget·intents
YungFan14 天前
SwiftUI-WebView 全面指南
ios·swiftui