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