- 已經了解了 SwiftUI 如何通過使用 @State 屬性包裝器將變化的資料存盤在結構體中,如何使用 $ 將狀態系結到UI控制元件的值,以及更改 @state 包裝的屬性時是如何自動讓 SwiftUI 重新呼叫結構體的 body屬性的,
- 所有這些結合在一起,可以撰寫如下代碼:
struct ContentView: View {
@State private var blurAmount: CGFloat = 0
var body: some View {
VStack {
Text("Hello, World!")
.blur(radius: blurAmount)
Slider(value: $blurAmount, in: 0...20)
}
}
}
- 如果執行這些代碼,將會發現左右拖動滑塊可以完全按照預期調整文本標簽的模糊量,現在,假設我們希望該系結不僅僅是處理模糊效果的半徑,想將其保存到 UserDefaults 中,運行一個方法,或者只是列印出該值以進行除錯,可以嘗試像這樣更新屬性:
@State private var blurAmount: CGFloat = 0 {
didSet {
print("New value is \(blurAmount)")
}
}
- 如果運行該代碼,可能會出現失望的結果:當拖動滑塊周圍時,會看到模糊量的變化,但是不會看到我們的 print() 陳述句被觸發,實際上什么都不會輸出,
- 為了了解這里的原因,可以考慮一下在使用 Core Data 時:使用 @FetchRequest 屬性包裝器查詢我們的資料,以及如何直接使用 FetchRequest 結構體,以便可以更好地控制它是如何創建的,
- 屬性包裝器具有該名稱,因為它們將我們的屬性包裝在另一個結構體中,對于許多屬性包裝器而言,該結構體與包裝器本身具有相同的名稱,但是使用 @FetchRequest 時展示了實際上是如何實際讀取其中的包裝值,獲取的結果,而不是請求本身,
- 這意味著當使用 @State 來包裝字串時,最終得到的實際屬性型別是 State,類似地,當使用 @Environment 和其他環境時,我們最終得到一個 Environment 型別的結構體,該結構體內部包含一些其他值,
- 之前曾解釋說,我們無法在視圖中修改屬性,因為它們是結構體,因此是固定的,但是,現在知道 @State 本身會生成一個結構體,因此面臨一個難題:如何修改該結構體?
- Xcode 有一個非常有用的命令,稱為“快速打開”(使用 Cmd + Shift + O 進行訪問),該命令可以在專案或已匯入的任何框架中找到任何檔案或型別,現在將其激活,然后輸入 “State”,希望第一個結果在其下方顯示 SwiftUI,但如果沒有,請找到并選擇它:

- 將進入 SwiftUI 生成的界面,該界面實質上是 SwiftUI 向我們展示的所有的部分,那里沒有實作代碼,只有協議,結構體,修飾符等的許多定義,
- 我們可以查看 state,因此應該被帶到此行:
@propertyWrapper public struct State<Value> : DynamicProperty {
- 該 @propertyWrapper 屬性使它成為 @State 供我們使用,現在往下看幾行,可以看到以下內容:
public var wrappedValue: Value { get nonmutating set }
- 該包裝值是我們要存盤的實際值,例如字串,這個生成的介面告訴我們,該屬性可以讀取(get)和寫入(set),但是當設定該值時,它實際上不會更改結構體本身,在后臺,它將值發送給SwiftUI以便存盤在可以自由修改的位置,因此,結構體本身永不改變,
- 現在知道了所有這些,讓我們回到問題代碼:
@State private var blurAmount: CGFloat = 0 {
didSet {
print("New value is \(blurAmount)")
}
}
- 在表面上,狀態為“ 當blurAmount 更改時,列印出它的新值”,但是,由于 @State 實際上會包裝其內容,因此實際上是說,當包裝 blurAmount 的 State 結構體更改時,請列印出新的模糊量,
- 還在這兒?現在讓我們更進一步:已經看到 State 如何使用一個非可變的 setter 包裝其值,這意味著 blurAmount 或包裝它的 State 結構體都沒有改變,我們的系結直接改變了內部存盤的值,這意味著屬性觀察者永遠不會被觸發,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/374815.html
標籤:其他
