SwiftUI 中的状态管理
2024-12-17 本文已影响0人
张_何
@State
-
@State
用于声明视图内部的可变状态,当状态发生变化时,SwiftUI
会自动重新渲染依赖该状态的视图
struct ContentView: View {
@State private var counter = 0
var body: some View {
VStack {
Text("Counter: \(counter)")
Button("Increment") {
counter += 1
}
}
}
}
上面点击“Increment”
按钮时Text
中的内容会跟着变化
@Binding
-
@Binding
用于在试图之间传递状态的引用,它允许父视图将某个状态传递给子视图,并且在子视图中修改这个状态时会影响父视图中的状态
struct HomepageView: View {
var body: some View {
ParentView()
}
}
struct ParentView: View {
@State private var counter = 0
var body: some View {
Text("ParentView Counter: \(counter)")
ChildView(counter: $counter)
}
}
struct ChildView: View {
@Binding var counter: Int
var body: some View {
Button("Increment in Child") {
counter += 1
}
}
}
当点击ChildView
中的"Increment in Child"
按钮时ParentView
中的counter
会跟着变化.
- 当然也可以多级传递,比如下面我们把
HomepageView
的counter
传递给ParentView
的counter1
, 然后再把ParentView
的counter2
传递给ChildView
的counter2
, 然后在ChildView
中点击"Increment in Child"
按钮的时候,HomepageView
和ParentView
中的Text
都会跟着变化
struct HomepageView: View {
@State private var counter = 0
var body: some View {
Text("HomepageView Counter: \(counter)")
ParentView(counter1: $counter)
}
}
struct ParentView: View {
@Binding var counter1: Int
var body: some View {
Text("ParentView Counter: \(counter1)")
ChildView(counter2: $counter1)
}
}
struct ChildView: View {
@Binding var counter2: Int
var body: some View {
Button("Increment in Child") {
counter2 += 1
}
}
}
@Published
-
@Publish
用于在ObervableObject
内声明一个可观察的属性,该属性的值发生变化时,所有观察该对象的视图都会更新 -
@Publish
不是直接用于视图的状态管理,而是用于声明一个ObservableObject
中的属性
,当该属性发生变化时,所有观察该对象的视图都会被更新。@Publish
使得属性变成可观察的,并且当值发生变化时,视图会自动刷新
ObservableObject
-
ObservableObject
是一个协议,用于实现数据模型的可观察性。它是SwiftUI
状态管理的一部分,使得模型对象能够在状态发生变化时通知依赖于它的视图进行更新。换句话说,ObservableObject
用于管理和存储应用程序中的数据,并确保在数据变化时,相关视图能够自动更新。 -
ObservableObject
协议要求实现一个或多个带有@Published
标记的属性,当这些属性的值发生变化时,任何观察该对象的视图都会重新渲染。
@ObservedObject
-
ObserveObject
用于观察外部对象的变化,通常与遵循ObservableObject
协议的对象一起使用,当对象的任何@Published
属性发生变化时,绑定视图会重新渲染。
class CounterModel: ObservableObject {
@Published var counter = 0
}
struct ContentView: View {
@ObservedObject var model = CounterModel()
var body: some View {
VStack {
Text("Counter: \(model.counter)")
Button("Increment") {
model.counter += 1
}
}
}
}
每次点击“Increment”
按钮时,Text
中的Counter
就会跟着变化
@EnvironmentObject
-
@EnvironmentObject
用于在视图层级结构中传递和共享数据,它允许你在多个视图之间传递共享的可观察对象ObservableObject
而不需要显式地将数据通过视图层级逐层传递。使用时,视图需要从环境中注入对象。
// 可观察对象,用来存储应用的状态
class UserSettings: ObservableObject {
@Published var username: String = "Guest"
}
struct ContentView: View {
@EnvironmentObject var userSettings: UserSettings // 从环境中获取共享的数据
var body: some View {
VStack {
Text("Hello, \(userSettings.username)") // 显示用户名
Button("Change Username in Content") {
userSettings.username = "Content" // 修改用户名,视图会自动更新
}
}
.padding()
}
}
struct AnotherView: View {
@EnvironmentObject var userSettings: UserSettings
var body: some View {
VStack {
Text("AnotherView Hello: \(userSettings.username)")
Button("Change Username in Another View") {
userSettings.username = "Another"
}
ThreeLevelView()
}
}
}
struct ThreeLevelView: View {
@EnvironmentObject var userSettings: UserSettings
var body: some View {
VStack {
Text("ThreeLevelView Hello: \(userSettings.username)")
Button("Change Username in ThreeLevelView") {
userSettings.username = "Three Level"
}
}
}
}
struct ParentView: View {
@EnvironmentObject var userSettings: UserSettings
var body: some View {
VStack {
ContentView()
AnotherView()
}
}
}
struct HomepageView: View {
@StateObject private var userSettings = UserSettings()
var body: some View {
VStack {
Text("HomePage Hello: \(userSettings.username)")
Button("Change Username in HomePage") {
userSettings.username = "HomePage"
}
}
ParentView().environmentObject(userSettings)
}
}
- 上面我们看到尽管在
ParentView
、ContentView
、AnotherView
、ThreeLevelView
中都是使用了userSettings
,但是我们只在生成ParentView
的时候注入了userSettings
,就可以在其子View
AnotherView
和ContentView
以及其孙View
ThreeLevelView
中就可以使用了。
@StateObject
-
@StateObject
是用于创建和持有一个ObservableObject
实例的属性包装器。它用于在视图中创建和拥有一个ObservableObject
实例,并确保视图在该对象的状态发生变化时自动更新。它确保对象在视图生命周期内保持持久性和唯一行,并不会因文视图重新渲染而丢失。 - 与
@ObservedObject
不同,@StateObject
是ObservableObject
实例的创建和持有者;而@ObservedObject
不会负责初始化或管理对象,@ObservedObject
只是@ObservedObject
是实例的观察者,只负责观察对象的变化
class Counter: ObservableObject {
@Published var value = 0
private var name: String
init(name: String) {
self.name = name
print("======\(name)")
}
}
struct ContentView: View {
@ObservedObject var counter: Counter
var body: some View {
VStack {
FirstView()
SecondView()
}
}
}
struct FirstView: View {
@ObservedObject private var counter = Counter(name: "FirstView")
var body: some View {
Text("First View")
ThirdView(name: "First", counter: counter)
}
}
struct SecondView: View {
@StateObject private var counter = Counter(name: "SecondView")
var body: some View {
Text("Second View")
ThirdView(name: "Second", counter: counter)
}
}
struct ThirdView: View {
@ObservedObject var counter: Counter
init(name: String, counter: Counter) {
print("-----\(name)")
self.counter = counter
}
var body: some View {
Text("Third View counter: \(counter.value)")
}
}
struct HomepageView: View {
@ObservedObject private var counter = Counter(name: "HomepageView")
var body: some View {
VStack {
ContentView(counter: counter)
Text("HomePage Hello: \(counter.value)")
Button("HomePage") {
counter.value += 1
}
}
}
}
点击Homepage Button
两次看到的结果
======HomepageView
======FirstView
-----First
======SecondView
-----Second
======FirstView
-----First
======FirstView
-----First
======FirstView
-----First
上面可以看到SecondView
只在创建的时候打印了一次,而FirstView
在每次点击“Homepage” button
的时候都会打印
@Environment
-
@Environment
属性包装器用于从视图的环境中获取系统提供的或由父视图注入的共享数据,而不需要显示地通过属性传递数据。它能让你访问与当前环境相关的信息,并在视图中进行响应式更新。 - 基本语法:
@Environment(\.key) var value@Environment(\.key) var value
key
:系统环境值的键或自定义的环境键,用来标识要获取的环境数据.
value
:存储在环境中的实际值,可以是任何类型. -
SwiftUI
提供了一些常用的系统级别的环境数据,比如: 当前的颜色模式,设备的横向布局类
struct ContentView: View {
@Environment(\.colorScheme) var colorScheme1 // 获取当前的颜色模式
@Environment(\.horizontalSizeClass) var sizeClass // 获取当前设备的横向布局类
var body: some View {
Text("Current color scheme is \(colorScheme1 == .dark ? "Dark" : "Light")")
.padding()
if sizeClass == .compact {
Text("Compact width class")
} else {
Text("Regular width class")
}
}
}
- 也可以使用@Environment 注入自定义数据
struct HomepageView: View {
var body: some View {
VStack {
ContentView()
}
}
}
// 定义一个简单的主题
struct AppTheme {
var backgroundColor: Color
var textColor: Color
}
struct AppThemeKey: EnvironmentKey {
// 为这个环境键提供默认值
static let defaultValue: AppTheme = AppTheme(backgroundColor: .white, textColor: .black)
}
extension EnvironmentValues {
var appTheme: AppTheme {
get { self[AppThemeKey.self] }
set { self[AppThemeKey.self] = newValue }
}
}
struct ContentView: View {
let lightTheme = AppTheme(backgroundColor: .white, textColor: .black)
let darkTheme = AppTheme(backgroundColor: .black, textColor: .white)
@State private var isDarkMode = false
var body: some View {
VStack {
Button("Toggle Theme") {
isDarkMode.toggle()
}
.padding()
ChildView()
}
.environment(\.appTheme, isDarkMode ? darkTheme : lightTheme) // 注入自定义的 AppTheme
}
}
struct ChildView: View {
@Environment(\.appTheme) var appTheme // 访问注入的 AppTheme
var body: some View {
VStack {
Text("This is a child view")
.padding()
.background(appTheme.backgroundColor) // 使用环境中传递的主题背景色
.foregroundColor(appTheme.textColor) // 使用环境中传递的主题文字色
}
}
}
上面每次点击“Toggle Theme” button
的时候,ChildView
中的Text
的文字和背景色就会改变
@AppStorage
-
@AppStorage
是一种属性包装器,用于将数据存储到应用的UserDefaults
中,并且能够使得数据在视图间保持同步。它允许你轻松地从UserDefaults
获取和存储数据,同时自动处理视图的更新。。它非常适合在应用程序中持久化小型设置或状态,如主题、语言选择等。当UserDefaults
中的数据发生变化时,视图会自动重新渲染。
struct HomepageView: View {
var body: some View {
VStack {
ContentView()
}
}
}
struct ContentView: View {
@AppStorage("isDarkMode") var isDarkMode: Bool = false
var body: some View {
VStack {
Button("Toggle Theme") {
isDarkMode.toggle()
}.padding()
ChildView()
}
}
}
struct ChildView: View {
@AppStorage("isDarkMode") var isDarkMode2: Bool = false
var body: some View {
VStack {
Text(isDarkMode2 ? "DarkModel" : "Not DarkModel")
}
}
}
上面每次点击Toggle Theme
的时候,会修改isDarkModel
的值,同时并保存到UserDefault
中去,当UserDefault
中的isDarkModel
变化时,会触发ChildView
重新渲染,这样就会导致ChildView
的内容跟着改变。