本文主要講述SwiftUI中的屬性包裝器,這些包裝器都是用來數據綁定的绷雏,作為視圖的唯一真值來源头滔,四種方式在實現功能上有細微差別。最后會進行總結比較涎显。
主要內容:
- State
- Binding
- ObservableObject
- EnvironmentObject
1. @State
SwiftUI管理聲明為state的存儲屬性坤检。當值發(fā)生變化時,SwiftUI會更新視圖層次結構中依賴于該值的部分期吓。使用@State作為存儲在視圖層次結構中的給定值的唯一真值來源早歇。
@State修飾的屬性雖然是存儲屬性,但是我們可以進行讀寫操作讨勤。
父視圖和子視圖進行傳遞該屬性只能是值傳遞箭跳。
需要在屬性名稱前加上一個美元符號$來獲得這個值。因為它是投影屬性
代碼:
struct ContentView: View {
@State private var str: String = ""
var body: some View {
VStack {
TextField("Placeholder", text: $str)
Text("\(str)")
}
}
}
說明:
- 在str上設置了@State修飾潭千,那么我在文本輸入框中輸入的數據谱姓,就會傳入到str中
- 同時str又綁定在文本視圖上,所以會將文本輸入框輸入的文本顯示到文本視圖上
- 這就是數據綁定的快捷實現刨晴。
注意:
- 不要在視圖層次結構中實例化視圖的位置初始化視圖的狀態(tài)屬性屉来,因為這可能與SwiftUI提供的存儲管理沖突路翻。
- 為了避免這種情況,總是將state聲明為private奶躯,并將其放在視圖層次結構中需要訪問該值的最高視圖中帚桩。
- 然后與任何也需要訪問的子視圖共享該狀態(tài),可以直接用于只讀訪問嘹黔,也可以作為讀寫訪問的綁定账嚎。
2. Binding
@State修飾的屬性是值傳遞,因此在父視圖和子視圖之間傳遞屬性時儡蔓。子視圖針對屬性的修改無法傳遞到父視圖上郭蕉。
Binding修飾后會將屬性會變?yōu)橐粋€引用類型,視圖之間的傳遞從值傳遞變?yōu)榱艘脗鬟f喂江,將父視圖和子視圖的屬性關聯起來召锈。這樣子視圖針對屬性的修改,會傳遞到父視圖上获询。
需要在屬性名稱前加上一個美元符號$來獲得這個值涨岁。因為它是投影屬性
下面代碼在主視圖上添加一個BtnView視圖,視圖上添加一個按鈕吉嚣,按鈕點擊后修改isShowText變量梢薪。這里的變量通過傳入參數與主視圖的isShowText進行綁定。綁定到主視圖的isShowText變量上尝哆。主視圖的變量用來決定文本視圖的隱藏和顯示秉撇。
代碼:
struct BtnView: View {
@Binding var isShowText: Bool
var body: some View {
Button {
isShowText.toggle()
} label: {
Text("點擊")
}
}
}
struct ContentView: View {
@State private var isShowText: Bool = true
var body: some View {
VStack {
if(isShowText) {
Text("點擊后會被隱藏")
} else {
Text("點擊后會被顯示").hidden()
}
BtnView(isShowText: $isShowText)
}
}
}
說明:
- 按鈕在BtnView視圖中,并且通過點擊秋泄,修改isShowText的值琐馆。
- 將BtnView視圖添加到ContentView上作為它的子視圖。并且傳入isShowText恒序。
- 此時的傳值是指針傳遞瘦麸,會將點擊后的屬性值傳遞到父視圖上。
- 父視圖拿到后也作用在自己的屬性歧胁,因此他的文本視圖會依據該屬性而隱藏或顯示
- 如果將@Binding改為@State瞎暑,會發(fā)現點擊后不起作用。這是因為值傳遞子視圖的更改不會反映到父視圖上
3. @ObservableObject
對實例進行監(jiān)聽与帆,其用處和@State非常相似了赌,只不過必須是對象,而且這個被監(jiān)聽的對象可以被多個視圖使用玄糟。需要注意用法
class DelayedUpdater: ObservableObject {
@Published var value = 0
init() {
for i in 1...10 {
DispatchQueue.main.asyncAfter(deadline: .now() + Double(i)) {
self.value += 1
}
}
}
}
struct ContentView: View {
@ObservedObject var updater = DelayedUpdater()
var body: some View {
VStack {
Text("\(updater.value)").padding()
}
}
}
說明:
- 綁定的數據是一個對象勿她。
- 被修飾的對象,其類必須遵守ObservableObject協議
- 此時這個類中被@Published修飾的屬性都會被綁定
- 使用@ObservedObject修飾這個對象阵翎,綁定這個對象逢并。
- 被@Published修飾的屬性發(fā)生改變時之剧,SwiftUI就會進行更新。
- 這里當value值會隨著時間發(fā)生改變砍聊。所以updater對象也會發(fā)生改變背稼。此時文本視圖的內容就會不斷更新。
4. @EnvironmentObject
在多視圖中玻蝌,為了避免數據的無效傳遞蟹肘,可以直接將數據放到環(huán)境中,供多個視圖進行使用俯树。
struct EnvView: View {
@EnvironmentObject var updater: DelayedUpdater
var body: some View {
Text("\(updater.value)")
}
}
struct BtnvView: View {
@EnvironmentObject var updater: DelayedUpdater
var body: some View {
Text("\(updater.value)")
}
}
struct ContentView: View {
let updater = DelayedUpdater()
var body: some View {
VStack {
EnvView().environmentObject(updater)
BtnvView().environmentObject(updater)
}
}
}
說明:
- 給屬性添加@EnvironmentObject修改帘腹,就將其放到了環(huán)境中。
- 其他視圖中想要獲取該屬性许饿,可以通過.environmentObject從環(huán)境中獲取阳欲。
- 可以看到分別將EnvView和BtnvView的屬性分別放到了環(huán)境中
- 之后我們ContentView視圖中獲取數據時,可以直接通過環(huán)境獲取陋率。
- 不需要將數據傳遞到ContentView球化,而是直接通過環(huán)境獲取,這樣避免了無效的數據傳遞瓦糟,更加高效
- 如果是在多層級視圖之間進行傳遞筒愚,會有更明顯的效果。
5. 總結
- @State將屬性和視圖進行綁定狸页,是唯一真實數據源。子視圖和父視圖之間是值傳遞
- @Binding在子視圖和父視圖之間是指針傳遞
- @ObservableObject只能監(jiān)聽對象扯再,并且可以在多個視圖中監(jiān)聽
- @EnvironmentObject將數據放到環(huán)境中芍耘,更適用在多視圖中