SwiftUI 屬性包裝器如何處理結(jié)構(gòu)體
本文轉(zhuǎn)載自微信公眾號「網(wǎng)羅開發(fā)」,作者韋弦Zhy。轉(zhuǎn)載本文請聯(lián)系網(wǎng)羅開發(fā)公眾號。
已經(jīng)了解了 SwiftUI 如何通過使用 @State 屬性包裝器將變化的數(shù)據(jù)存儲在結(jié)構(gòu)體中,如何使用 $ 將狀態(tài)綁定到UI控件的值,以及更改 @state 包裝的屬性時是如何自動讓 SwiftUI 重新調(diào)用我們的結(jié)構(gòu)體的 body屬性的。
所有這些結(jié)合在一起,使我們可以編寫如下代碼:
- 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)
- }
- }
- }
如果您執(zhí)行這些代碼,將會發(fā)現(xiàn)左右拖動滑塊可以完全按照預期調(diào)整文本標簽的模糊量。
現(xiàn)在,假設(shè)我們希望該綁定不僅僅是處理模糊效果的半徑。也許我們想將其保存到 UserDefaults 中,運行一個方法,或者只是打印出該值以進行調(diào)試。您可以嘗試像這樣更新屬性:
- @State private var blurAmount: CGFloat = 0 {
- didSet {
- print("New value is \(blurAmount)")
- }
- }
如果您運行該代碼,您將感到失望:當您拖動滑塊周圍時,您會看到模糊量的變化,但是您不會看到我們的 print() 語句被觸發(fā)——實際上,什么都不會輸出。
為了了解這里發(fā)生的事情,我希望您考慮一下我們在使用 Core Data 時:我們使用 @FetchRequest 屬性包裝器查詢我們的數(shù)據(jù),但我還向您展示了如何直接使用 FetchRequest 結(jié)構(gòu)體,以便我們可以更好地控制它是如何創(chuàng)建的。
屬性包裝器具有該名稱,因為它們將我們的屬性包裝在另一個結(jié)構(gòu)體中。對于許多屬性包裝器而言,該結(jié)構(gòu)體與包裝器本身具有相同的名稱,但是使用 @FetchRequest 時我向您展示了我們實際上是如何實際讀取其中的包裝值——獲取的結(jié)果,而不是請求本身。
這意味著當我們使用 @State 來包裝字符串時,最終得到的實際屬性類型是 State
之前我曾解釋說,我們無法在視圖中修改屬性,因為它們是結(jié)構(gòu)體,因此是固定的。但是,現(xiàn)在您知道 @State 本身會生成一個結(jié)構(gòu)體,因此我們面臨一個難題:如何修改該結(jié)構(gòu)體?
Xcode 有一個非常有用的命令,稱為“快速打開”(使用 Cmd + Shift + O 進行訪問),該命令使您可以在項目或已導入的任何框架中找到任何文件或類型。現(xiàn)在將其激活,然后輸入 "State"——希望第一個結(jié)果在其下方顯示 SwiftUI,但如果沒有,請找到并選擇它。
您將進入 SwiftUI 生成的界面,該界面實質(zhì)上是 SwiftUI 向我們展示的所有的部分。那里沒有實現(xiàn)代碼,只有協(xié)議,結(jié)構(gòu)體,修飾符等的許多定義。
我們要求查看 state,因此您應(yīng)該被帶到此行:
- @propertyWrapper public struct State : DynamicProperty {
該 @propertyWrapper 屬性使它成為 @State 供我們使用。
現(xiàn)在往下看幾行,您應(yīng)該看到以下內(nèi)容:
- public var wrappedValue: Value { get nonmutating set }
該包裝值是我們要存儲的實際值,例如字符串。這個生成的接口告訴我們,該屬性可以讀取(get)和寫入(set),但是當我們設(shè)置該值時,它實際上不會更改結(jié)構(gòu)體本身。在后臺,它將值發(fā)送給SwiftUI以便存儲在可以自由修改的位置,因此,結(jié)構(gòu)體本身永不改變。
現(xiàn)在您已經(jīng)知道了所有這些,讓我們回到我們的問題代碼:
- @State private var blurAmount: CGFloat = 0 {
- didSet {
- print("New value is \(blurAmount)")
- }
- }
在表面上,狀態(tài)為“ 當blurAmount 更改時,打印出它的新值。”但是,由于 @State 實際上會包裝其內(nèi)容,因此實際上是說,當包裝 blurAmount 的 State 結(jié)構(gòu)體更改時,請打印出新的模糊量。
還在這兒?現(xiàn)在讓我們更進一步:您已經(jīng)看到 State 如何使用一個非可變的 setter 包裝其值,這意味著 blurAmount 或包裝它的 State 結(jié)構(gòu)體都沒有改變——我們的綁定直接改變了內(nèi)部存儲的值,這意味著屬性觀察者永遠不會被觸發(fā)。
那么我們該如何解決——我們?nèi)绾螌⒁恍┕δ芨郊拥桨b的屬性上?為此,我們需要自定義綁定——讓我們接下來看看...
> 譯自 How property wrappers become structs[1]
參考資料
[1]How property wrappers become structs: https://www.hackingwithswift.com/books/ios-swiftui/how-property-wrappers-become-structs