SwiftUI常见的属性包装器,在什么场景中使用?

前言

在 SwiftUI 中,属性用于增强和管理视图的状态,以及处理视图与数据模型之间的绑定和交互。 常见的属性包装器有 @State@Binding@Published@StateObject@EnvironmentObject

@State说明

@State为属性提供了一种声明性的方式来更新视图,并且会自动监视其值的变化以进行视图刷新。

在视图结构体中声明一个属性,并用@State属性包装器进行修饰。

swift 复制代码
struct ContentView: View {
    @State private var count = 0
    
    var body: some View {
        Text("Count: \(count)")
        Button(action: {
            count += 1
        }) {
            Text("Increment")
        }
        
    }
}

还可以将@State属性包装器与其他视图元素一起使用,例如文本字段、滑块等。如下所示,我们通过$name绑定了给了TextField,确保在用户输入的内容变化时,得到输入框的内容。

swift 复制代码
struct ContentView: View {
    @State private var name = ""
    
    var body: some View {
        VStack {
            TextField("Enter your name", text: $name)
            
            Text("Hello, \(name)!")
        }
    }
}

@Binding说明

@Binding用于在视图之间传递和共享数据。它提供了一种同步更新数据的机制,使得不同视图中的数据始终保持一致。比如我们将父视图A的值,传递给子视图B。在B视图的更新的时候告诉父视图。那么我们就可以采用此方式。

swift 复制代码
struct ParentView: View {
    @State private var isToggleOn = false
    
    var body: some View {
        ChildView(isToggleOn: $isToggleOn)
    }
}

struct ChildView: View {
    @Binding var isToggleOn: Bool
    
    var body: some View {
        Toggle(isOn: $isToggleOn) {
            Text("Toggle")
        }
        
        if isToggleOn {
            Text("Toggle is on")
        } else {
            Text("Toggle is off")
        }
    }
}

@Published说明

@Published用于标记那些需要被视图自动观察并响应变化的类属性。在一个遵循 ObservableObject 协议的类中使用 @Published 修饰符时,任何对这个属性的修改都会触发该对象的 objectWillChange 发布通知,进而导致所有绑定到这些属性的 SwiftUI 视图自动刷新。

less 复制代码
// 定义一个遵循 ObservableObject 协议的模型类
class MyModel: ObservableObject {
    @Published var name: String = "初始名字"
    @Published var count: Int = 0
}

// 在 SwiftUI 视图中使用 ObservedObject 来引用这个模型
struct MyView: View {
    @ObservedObject var model: MyModel
    
    var body: some View {
        VStack {
            TextField("输入名字", text: $model.name)
            Text("名字是:\(model.name)")
            
            Button("点击计数") {
                model.count += 1
            }
            Text("点击次数:\(model.count)")
        }
    }
}

// 创建模型实例并在视图中使用
struct ContentView: View {
    let myModel = MyModel()
    
    var body: some View {
        MyView(model: myModel)
    }
}

@StateObjec说明

@StateObject 用于管理类实例生命周期的一个属性包装器,它在 SwiftUI 视图的生命周期中创建并维护一个指定类型的单例对象。这个对象遵循 ObservableObject 协议,其目的是确保即使视图被重建或重新创建(例如由于导航、设备旋转或者状态更改),关联的对象也会持续存在,并且其状态保持不变。

@StateObject 主要用于那些需要持久化存在的数据模型或者是网络请求、定时器等异步操作的结果。当使用 @StateObject 时,SwiftUI 会在首次创建视图时初始化该对象,并在后续视图重建过程中复用同一对象实例。

如果因为某些原因 ContentView 被重新创建,比如从后台返回到前台,@StateObject 会确保同一个 viewModel 实例被再次使用,而不是创建一个新的实例,从而保持了视图的状态一致性。

swift 复制代码
class MyViewModel: ObservableObject {
    @Published var message = "初始消息"
    
    init() {
        // 可能进行的数据加载、网络请求或其他初始化工作
    }
}

struct ContentView: View {
    @StateObject private var viewModel = MyViewModel() // 创建并维护 MyViewModel 的实例
    
    var body: some View {
        VStack {
            Text("消息:\(viewModel.message)")
            
            Button("更新消息") {
                viewModel.message = "新消息"
            }
        }
    }
}

@EnvironmentObject说明

@EnvironmentObject 允许在视图层级中共享一个遵循 ObservableObject 协议的对象。这个对象的状态变化可以触发相关联的视图更新。通过在环境(environment)中注入这些对象,你可以在多个视图间轻松传递和共享数据。简单来说它相当于一个全局变量的声明

使用 @EnvironmentObject 的基本步骤:

  1. 创建一个遵循 ObservableObject 的类,并定义你需要共享的数据模型或状态。
  2. 在你的应用根视图或者某个高层级视图中设置环境对象。这通常通过 .environmentObject() 视图修饰符来实现。
  3. 在需要访问该环境对象的子视图中声明 @EnvironmentObject 属性。
scss 复制代码
class UserSettings: ObservableObject {
    @Published var userName = "默认用户"
}

// 设置环境对象的根视图
struct ContentView: View {
    @StateObject var userSettings = UserSettings()
    
    var body: some View {
        VStack {
            Text("欢迎来到主界面!")
            
            NavigationLink(destination: SubView()) {
                Text("跳转到子视图")
            }
        }
        .environmentObject(userSettings) // 将 UserSettings 对象放入环境
    }
}

// 子视图从环境中获取并显示用户设置
struct SubView: View {
    @EnvironmentObject var userSettings: UserSettings
    
    var body: some View {
        VStack {
            Text("用户名:\(userSettings.userName)")
            
            Button("更改用户名") {
                userSettings.userName = "新用户名"
            }
        }
    }
}

总结

@State 属性包装器在 SwiftUI 中扮演着关键角色,它实现了属性与视图间的强绑定关系。这意味着,被 @State 修饰的属性被视为视图层次中的唯一且真实的数据源。当这个状态发生变化时,SwiftUI 能够自动触发相关联视图的刷新以反映最新的数据状态。在视图层级结构中,父视图和子视图间的数据传递是基于值传递的方式进行的,即子视图通过获取父视图 @State 变量的一个副本来展示和操作其内容。

@Binding 属性包装器则提供了一种更为灵活的父子视图间通信机制。不同于值传递,它是通过指针(引用)的方式来实现双向数据绑定。这样,在子视图内部对 @Binding 类型变量的修改会直接反映到父视图的原始状态上,确保了数据的一致性。

@ObservableObject 是一个用于对象级别的响应式编程工具。遵循该协议的对象能够在其属性发生改变时发送通知给所有相关的观察者。这种机制使得多个视图可以监听同一个对象的状态变化,并据此做出相应的更新。这在复杂的视图结构和数据模型中尤为有用,因为它允许你在不同视图之间共享并实时同步同一份数据。

最后,@EnvironmentObject 则为全局数据共享提供了便利途径。它将数据封装到环境对象中,并在整个视图层级结构中均可访问。通过 .environmentObject() 方法注入环境对象后,任意嵌套深度的视图都可以声明并使用 @EnvironmentObject 来获取这些共享数据。这一特性使 @EnvironmentObject 成为管理多视图间共享状态的理想选择,尤其在需要跨越多个视图层级传递或共享数据的情况下。

相关推荐
Swift社区20 小时前
Apple 新品发布会亮点有哪些 | Swift 周报 issue 61
ios·swiftui·swift
humiaor2 天前
Xcode报错:No exact matches in reference to static method ‘buildExpression‘
swiftui·xcode
humiaor10 天前
Xcode报错:Return from initializer without initializing all stored properties
swiftui·binding
2401_854391081 个月前
SwiftUI 革命:打造未来派用户界面的艺术
ui·ios·swiftui
Swift社区2 个月前
Swift 中的函数式核心与命令式外壳:单向数据流
ios·swiftui·swift
东坡肘子2 个月前
肘子的 Swift 周报 #042| 经验是柄双刃剑
swiftui·swift·apple
Swift社区2 个月前
SwiftUI 中掌握 ScrollView 的使用:滚动可见性
ios·swiftui·swift
2401_857424522 个月前
SwiftUI革新:Xcode UI开发的新纪元
ui·swiftui·xcode
Swift社区2 个月前
SwiftUI 在 WWDC 24 之后的新变化
ios·swiftui·wwdc
Swift社区2 个月前
苹果将为 Apple Watch X 铺路 | Swift 周报 issue 45
ios·swiftui·swift