30天学会Swift-23:SwiftUI-数据流与环境

学习目标

  • 深入理解SwiftUI中的数据流管理,包括@State, @Binding, @ObservedObject, @StateObject, @EnvironmentObject
  • 掌握如何使用ObservableObject@Published创建可观察对象。
  • 理解@Environment@EnvironmentObject在跨视图传递数据中的作用。
  • 学习如何选择合适的数据流管理方式。

学习内容

1. SwiftUI 数据流概览

SwiftUI 的核心是数据驱动 UI。当数据发生变化时,UI 会自动更新。为了实现这一目标,SwiftUI 提供了多种属性包装器(Property Wrappers)来管理不同类型的数据流。

2. 属性包装器 (Property Wrappers)

2.1 @State
  • 用途:用于管理视图内部的简单值类型数据(如 Int, String, Bool)。当 @State 变量改变时,视图会重新渲染。
  • 特点:数据所有权在当前视图,通常声明为 private
  • 示例
    struct CounterView: View {
        @State private var count = 0
        
        var body: some View {
            VStack {
                Text("Count: \(count)")
                Button("Increment") {
                    count += 1
                }
            }
        }
    }
    
2.2 @Binding
  • 用途:在父视图和子视图之间建立双向绑定。子视图可以读取和修改父视图的 @State 或其他 @Binding 变量,而无需拥有数据所有权。
  • 特点:不拥有数据,只是引用。通过 $ 符号创建绑定。
  • 示例
    struct ParentView: View {
        @State private var isOn = false
        
        var body: some View {
            VStack {
                Toggle(isOn: $isOn) {
                    Text("Parent Toggle: \(isOn ? "On" : "Off")")
                }
                ChildToggleView(toggleState: $isOn) // 传递绑定
            }
        }
    }
    
    struct ChildToggleView: View {
        @Binding var toggleState: Bool // 接收绑定
        
        var body: some View {
            Toggle(isOn: $toggleState) {
                Text("Child Toggle: \(toggleState ? "On" : "Off")")
            }
        }
    }
    
2.3 @ObservedObjectObservableObject
  • 用途:用于管理引用类型数据(通常是 class),当数据改变时需要通知 UI 更新。
  • ObservableObject:一个协议,你的数据模型类需要遵循它。
  • @Published:在 ObservableObject 内部,标记需要发布变化的属性。当 @Published 属性改变时,ObservableObject 会自动发送通知。
  • @ObservedObject:在视图中使用,用于监听 ObservableObject 实例的变化。当 ObservableObject 发布变化时,使用它的视图会重新渲染。
  • 特点:视图不拥有数据,数据由外部创建并传入。适用于数据生命周期与视图生命周期不完全一致的情况。
  • 示例
    class UserSettings: ObservableObject {
        @Published var username = "Guest"
        @Published var score = 0
    }
    
    struct UserProfileView: View {
        @ObservedObject var settings = UserSettings() // 视图监听这个对象
        
        var body: some View {
            VStack {
                Text("Username: \(settings.username)")
                Text("Score: \(settings.score)")
                Button("Change Username") {
                    settings.username = "John Doe"
                }
                Button("Increase Score") {
                    settings.score += 10
                }
            }
        }
    }
    
2.4 @StateObject
  • 用途:与 @ObservedObject 类似,也用于管理引用类型数据。但 @StateObject 拥有数据的生命周期,它会在视图首次创建时初始化对象,并在视图生命周期内保持该对象。
  • 特点:视图拥有数据。适用于数据模型与视图生命周期紧密绑定,且视图是数据模型唯一所有者的情况。
  • 示例
    struct UserProfileViewWithStateObject: View {
        @StateObject var settings = UserSettings() // 视图拥有这个对象
        
        var body: some View {
            VStack {
                Text("Username: \(settings.username)")
                Text("Score: \(settings.score)")
                Button("Change Username") {
                    settings.username = "Jane Doe"
                }
                Button("Increase Score") {
                    settings.score += 5
                }
            }
        }
    }
    
2.5 @EnvironmentObject
  • 用途:用于在视图层级结构中传递共享数据,避免手动通过构造器层层传递数据(“钻孔”问题)。
  • 特点:数据由祖先视图注入到环境中,子孙视图通过 @EnvironmentObject 从环境中获取。数据必须是 ObservableObject
  • 注入:使用 .environmentObject() 修饰符注入。
  • 获取:使用 @EnvironmentObject 属性包装器获取。
  • 示例
    class AppData: ObservableObject {
        @Published var themeColor: Color = .blue
        @Published var isLoggedIn: Bool = false
    }
    
    struct ContentView: View {
        @StateObject var appData = AppData() // 在根视图创建并注入
        
        var body: some View {
            NavigationView {
                VStack {
                    Text("Welcome!")
                        .font(.largeTitle)
                        .foregroundColor(appData.themeColor)
                    
                    NavigationLink(destination: SettingsView())
                    {
                        Text("Go to Settings")
                    }
                    .padding()
                }
                .navigationTitle("Home")
            }
            .environmentObject(appData) // 注入到环境中
        }
    }
    
    struct SettingsView: View {
        @EnvironmentObject var appData: AppData // 从环境中获取
        
        var body: some View {
            VStack {
                Toggle(isOn: $appData.isLoggedIn) {
                    Text("Logged In")
                }
                ColorPicker("Theme Color", selection: $appData.themeColor)
            }
            .navigationTitle("Settings")
            .padding()
        }
    }
    

3. @Environment

  • 用途:用于访问SwiftUI提供的预定义环境值,如colorScheme(深色/浅色模式)、locale(本地化设置)、isPresented(视图是否被呈现)等。
  • 特点:只读,不能修改。
  • 示例
    struct EnvironmentExampleView: View {
        @Environment(\.colorScheme) var colorScheme // 获取颜色模式
        @Environment(\.isPresented) var isPresented // 获取视图是否被呈现
        
        var body: some View {
            VStack {
                Text("Current Color Scheme: \(colorScheme == .dark ? "Dark" : "Light")")
                Text("Is Presented: \(isPresented ? "Yes" : "No")")
            }
        }
    }
    

4. 数据流选择指南

  • @State:视图私有、简单值类型数据。
  • @Binding:父子视图间双向传递数据,子视图不拥有数据。
  • @StateObject:视图拥有并管理引用类型数据,数据生命周期与视图绑定。
  • @ObservedObject:视图监听外部传入的引用类型数据,视图不拥有数据。
  • @EnvironmentObject:在视图层级结构中共享引用类型数据,避免层层传递。

实践练习

  1. 使用@State@Binding
    • 创建一个LoginView,包含用户名和密码的TextField以及一个登录Button
    • 使用@State管理用户名和密码。
    • 创建一个SecureField(密码输入框),并使用@Binding将其值与LoginView中的密码@State变量绑定。
  2. 使用@StateObject@ObservedObject
    • 创建一个TaskStore类,遵循ObservableObject,包含一个@Publishedtasks: [String]数组。
    • ContentView中使用@StateObject创建TaskStore实例。
    • ContentView中显示任务列表,并添加一个按钮来添加新任务。
    • 创建一个TaskDetailView,接收一个@ObservedObjectTaskStore实例,并显示任务数量。
  3. 使用@EnvironmentObject
    • 创建一个ThemeSettings类,遵循ObservableObject,包含一个@PublishedaccentColor: Color属性。
    • 在应用程序的根视图(例如[YourAppName]App.swift中的ContentView)注入ThemeSettings作为environmentObject
    • 在应用程序的多个子视图中,通过@EnvironmentObject获取ThemeSettings,并使用accentColor来设置文本颜色或按钮颜色。

思考题

  • @StateObject@ObservedObject的主要区别是什么?在什么场景下应该优先选择哪个?
  • @EnvironmentObject解决了什么问题?它和手动传递@ObservedObject有什么优缺点?
  • 在设计SwiftUI应用程序的数据流时,你通常会遵循哪些原则?
【课程特点】1、231节大容量课程:包含了SwiftUI的大部分知识点,详细讲解SwiftUI的方方面面;2、15个超级精彩的实例:包含美食、理财、健身、教育、电子商务等各行业的App实例;3、创新的教学模式:手把手教您SwiftUI用户界面开发技术,一看就懂,一学就会;4、贴心的操作提示:让您的眼睛始终处于操作的焦点位置,不用再满屏找光标;5、语言简洁精练:瞄准问题的核心所在,减少对思维的干扰,并节省您宝贵的时间;6、视频短小精悍:即方便于您的学习和记忆,也方便日后对功能的检索;7、齐全的学习资料:提供所有课程的源码; 更好的应用,更少的代码!SwiftUI是苹果主推的下一代用户界面搭建技术,具有声明式语法、实时生成界面预览等特性,可以为苹果手机、苹果平板、苹果电脑、苹果电视、苹果手表五个平台搭建统一的用户界面。SwiftUI是一种创新、简单的iOS开发中的界面布局方案,可以通过Swift语言的强大功能,在所有的Apple平台上快速构建用户界面。 仅使用一组工具和API为任何Apple设备构建用户界面。SwiftUI具有易于阅读和自然编写的声明式Swift语法,可新的Xcode设计工具无缝协作,使您的代码和设计**同步。自动支持动态类型、暗黑模式、本地化和可访问性,意味着您的**行SwiftUI代码已经是您编写过的非常强大的UI代码了。 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明似水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值