@State
-
@State用于聲明視圖內(nèi)部的可變狀態(tài),當(dāng)狀態(tài)發(fā)生變化時(shí),SwiftUI會(huì)自動(dòng)重新渲染依賴該狀態(tài)的視圖
struct ContentView: View {
@State private var counter = 0
var body: some View {
VStack {
Text("Counter: \(counter)")
Button("Increment") {
counter += 1
}
}
}
}
上面點(diǎn)擊“Increment” 按鈕時(shí)Text中的內(nèi)容會(huì)跟著變化
@Binding
-
@Binding用于在試圖之間傳遞狀態(tài)的引用,它允許父視圖將某個(gè)狀態(tài)傳遞給子視圖,并且在子視圖中修改這個(gè)狀態(tài)時(shí)會(huì)影響父視圖中的狀態(tài)
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
}
}
}
當(dāng)點(diǎn)擊ChildView中的"Increment in Child" 按鈕時(shí)ParentView中的counter會(huì)跟著變化.
- 當(dāng)然也可以多級(jí)傳遞,比如下面我們把
HomepageView的counter傳遞給ParentView的counter1, 然后再把ParentView的counter2傳遞給ChildView的counter2, 然后在ChildView中點(diǎn)擊"Increment in Child"按鈕的時(shí)候,HomepageView和ParentView中的Text都會(huì)跟著變化
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內(nèi)聲明一個(gè)可觀察的屬性,該屬性的值發(fā)生變化時(shí),所有觀察該對(duì)象的視圖都會(huì)更新 -
@Publish不是直接用于視圖的狀態(tài)管理,而是用于聲明一個(gè)ObservableObject中的屬性
,當(dāng)該屬性發(fā)生變化時(shí),所有觀察該對(duì)象的視圖都會(huì)被更新。@Publish使得屬性變成可觀察的,并且當(dāng)值發(fā)生變化時(shí),視圖會(huì)自動(dòng)刷新
ObservableObject
-
ObservableObject是一個(gè)協(xié)議,用于實(shí)現(xiàn)數(shù)據(jù)模型的可觀察性。它是SwiftUI狀態(tài)管理的一部分,使得模型對(duì)象能夠在狀態(tài)發(fā)生變化時(shí)通知依賴于它的視圖進(jìn)行更新。換句話說(shuō),ObservableObject用于管理和存儲(chǔ)應(yīng)用程序中的數(shù)據(jù),并確保在數(shù)據(jù)變化時(shí),相關(guān)視圖能夠自動(dòng)更新。 -
ObservableObject協(xié)議要求實(shí)現(xiàn)一個(gè)或多個(gè)帶有@Published標(biāo)記的屬性,當(dāng)這些屬性的值發(fā)生變化時(shí),任何觀察該對(duì)象的視圖都會(huì)重新渲染。
@ObservedObject
-
ObserveObject用于觀察外部對(duì)象的變化,通常與遵循ObservableObject協(xié)議的對(duì)象一起使用,當(dāng)對(duì)象的任何@Published屬性發(fā)生變化時(shí),綁定視圖會(huì)重新渲染。
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
}
}
}
}
每次點(diǎn)擊“Increment”按鈕時(shí),Text中的Counter就會(huì)跟著變化
@EnvironmentObject
-
@EnvironmentObject用于在視圖層級(jí)結(jié)構(gòu)中傳遞和共享數(shù)據(jù),它允許你在多個(gè)視圖之間傳遞共享的可觀察對(duì)象ObservableObject而不需要顯式地將數(shù)據(jù)通過(guò)視圖層級(jí)逐層傳遞。使用時(shí),視圖需要從環(huán)境中注入對(duì)象。
// 可觀察對(duì)象,用來(lái)存儲(chǔ)應(yīng)用的狀態(tài)
class UserSettings: ObservableObject {
@Published var username: String = "Guest"
}
struct ContentView: View {
@EnvironmentObject var userSettings: UserSettings // 從環(huán)境中獲取共享的數(shù)據(jù)
var body: some View {
VStack {
Text("Hello, \(userSettings.username)") // 顯示用戶名
Button("Change Username in Content") {
userSettings.username = "Content" // 修改用戶名,視圖會(huì)自動(dòng)更新
}
}
.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的時(shí)候注入了userSettings,就可以在其子ViewAnotherView和ContentView以及其孫ViewThreeLevelView中就可以使用了。
@StateObject
-
@StateObject是用于創(chuàng)建和持有一個(gè)ObservableObject實(shí)例的屬性包裝器。它用于在視圖中創(chuàng)建和擁有一個(gè)ObservableObject實(shí)例,并確保視圖在該對(duì)象的狀態(tài)發(fā)生變化時(shí)自動(dòng)更新。它確保對(duì)象在視圖生命周期內(nèi)保持持久性和唯一行,并不會(huì)因文視圖重新渲染而丟失。 - 與
@ObservedObject不同,@StateObject是ObservableObject實(shí)例的創(chuàng)建和持有者;而@ObservedObject不會(huì)負(fù)責(zé)初始化或管理對(duì)象,@ObservedObject只是@ObservedObject是實(shí)例的觀察者,只負(fù)責(zé)觀察對(duì)象的變化
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
}
}
}
}
點(diǎn)擊Homepage Button兩次看到的結(jié)果
======HomepageView
======FirstView
-----First
======SecondView
-----Second
======FirstView
-----First
======FirstView
-----First
======FirstView
-----First
上面可以看到SecondView 只在創(chuàng)建的時(shí)候打印了一次,而FirstView在每次點(diǎn)擊“Homepage” button的時(shí)候都會(huì)打印
@Environment
-
@Environment屬性包裝器用于從視圖的環(huán)境中獲取系統(tǒng)提供的或由父視圖注入的共享數(shù)據(jù),而不需要顯示地通過(guò)屬性傳遞數(shù)據(jù)。它能讓你訪問(wèn)與當(dāng)前環(huán)境相關(guān)的信息,并在視圖中進(jìn)行響應(yīng)式更新。 - 基本語(yǔ)法:
@Environment(\.key) var value@Environment(\.key) var value
key:系統(tǒng)環(huán)境值的鍵或自定義的環(huán)境鍵,用來(lái)標(biāo)識(shí)要獲取的環(huán)境數(shù)據(jù).
value:存儲(chǔ)在環(huán)境中的實(shí)際值,可以是任何類型. -
SwiftUI提供了一些常用的系統(tǒng)級(jí)別的環(huán)境數(shù)據(jù),比如: 當(dāng)前的顏色模式,設(shè)備的橫向布局類
struct ContentView: View {
@Environment(\.colorScheme) var colorScheme1 // 獲取當(dāng)前的顏色模式
@Environment(\.horizontalSizeClass) var sizeClass // 獲取當(dāng)前設(shè)備的橫向布局類
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 注入自定義數(shù)據(jù)
struct HomepageView: View {
var body: some View {
VStack {
ContentView()
}
}
}
// 定義一個(gè)簡(jiǎn)單的主題
struct AppTheme {
var backgroundColor: Color
var textColor: Color
}
struct AppThemeKey: EnvironmentKey {
// 為這個(gè)環(huán)境鍵提供默認(rèn)值
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 // 訪問(wèn)注入的 AppTheme
var body: some View {
VStack {
Text("This is a child view")
.padding()
.background(appTheme.backgroundColor) // 使用環(huán)境中傳遞的主題背景色
.foregroundColor(appTheme.textColor) // 使用環(huán)境中傳遞的主題文字色
}
}
}
上面每次點(diǎn)擊“Toggle Theme” button的時(shí)候,ChildView中的Text的文字和背景色就會(huì)改變
@AppStorage
-
@AppStorage是一種屬性包裝器,用于將數(shù)據(jù)存儲(chǔ)到應(yīng)用的UserDefaults中,并且能夠使得數(shù)據(jù)在視圖間保持同步。它允許你輕松地從UserDefaults獲取和存儲(chǔ)數(shù)據(jù),同時(shí)自動(dòng)處理視圖的更新。。它非常適合在應(yīng)用程序中持久化小型設(shè)置或狀態(tài),如主題、語(yǔ)言選擇等。當(dāng)UserDefaults中的數(shù)據(jù)發(fā)生變化時(shí),視圖會(huì)自動(dòng)重新渲染。
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")
}
}
}
上面每次點(diǎn)擊Toggle Theme 的時(shí)候,會(huì)修改isDarkModel的值,同時(shí)并保存到UserDefault中去,當(dāng)UserDefault中的isDarkModel變化時(shí),會(huì)觸發(fā)ChildView重新渲染,這樣就會(huì)導(dǎo)致ChildView 的內(nèi)容跟著改變。