Data Flow Through SwiftUI - SwiftUI 中的數(shù)據(jù)流
摘要
如何讓數(shù)據(jù)的變化反映到最終的用戶界面。當用戶或外部的事件改變了數(shù)據(jù),如何操作這些數(shù)據(jù)流?
Principles of Data Flow - 數(shù)據(jù)流的原則
數(shù)據(jù)是所有驅(qū)動 UI 的信息。SwiftUI 為數(shù)據(jù)流提供了一系列工具:Property,@Environment,BindableObject,@State,@Binding。

開發(fā)者過去需要小心的把變化的數(shù)據(jù)同步到各種視圖和他們的子視圖,這通常會使得代碼變得復雜和易出 bug。SwiftUI 引入了 Single Source of truth,即單一信息源的原則。位于父視圖中的屬性,可以被子視圖訪問。

@State - 狀態(tài)
@State 就是 Source of truth。
在 View 的 struct 里聲明屬性時,使用 @State 關鍵字表示視圖中的 UI 元素會依賴該屬性的值動態(tài)改變。當屬性值變化時,SwiftUI 會使用新的值重新從 body 生成一個新的 View,達到數(shù)據(jù)改變映射到用戶界面的效果。
@State 是一個“Property Wrapper”,即屬性包裝。當一個屬性使用 @State 關鍵詞時,Swift 會為這個屬性添加一些額外的操作,比如 Bool 類型可以獲得 toggle() 方法,用于在 true/false 之間切換。
視圖經(jīng)常會被系統(tǒng)重建,所以需要給屬性賦一個默認的值。若屬性被標記為 @State,系統(tǒng)會使用儲存的變量的值,而不是每次都使用初始化的值。
同時,為了保證運行效率,系統(tǒng)會比較值改變導致哪些視圖需要被重新渲染,最后只重新渲染那些需要更新的視圖。

數(shù)據(jù)流向是單向的:用戶產(chǎn)生一個交互事件,導致視圖中某個 @State 的屬性發(fā)生變化,然后 SwiftUI 更新對應的 View,最后把渲染后的界面反饋給用戶。

@Binding - 綁定
@Binding 為子視圖提供讀/寫 Source of truth 的方法
如果要將子視圖打包成一個新的自定義視圖,同時又依賴了父視圖的 @State 的屬性,那么這種情況不應該在子視圖里使用 @State 來標記屬性。因為使用 @State 會導致子視圖中的屬性成為新的 Source of truth。
子視圖的渲染依賴父視圖的屬性,所以它不應該有自己的 Source of truth。它只需要獲取父視圖的這個屬性,然后修改它。
這種情況,應該要在子視圖中使用 @Binding。

@Binding 標記的屬性不需要一個初始化的值,因為這個值會從父視圖的 @State 屬性自動生成。

只需要在父視圖中,聲明子視圖的時候傳入帶美元符號的屬性名即可完成綁定。父視圖依舊持有 @State 這個 Source of truth,同時子視圖可以通過綁定訪問父視圖的屬性。
通過這種優(yōu)雅的數(shù)據(jù)流控制方式,我們不再需要 ViewController 了!
某些事件是從外部發(fā)生的,例如:計時器,通知。

在視圖中添加類似于?.onRecive() 的?Modifier 可以監(jiān)聽外部事件。外部數(shù)據(jù)會傳入到閉包中,可以直接在閉包中通過 “self.屬性 = 新的值” 來修改視圖的 Source of truth 數(shù)據(jù),達到修改視圖的效果。

可以通過在定義 class 的時候?qū)崿F(xiàn) BindableObject 協(xié)議,使得對象能與 SwiftUI 中的視圖綁定。當 SwiftUI 中的 model 發(fā)生改變時,也會將改變反映在視圖中。


@EnvironmentObject
@EnvironmentObject 是一個比較全局的對象。它可以跨視圖讓數(shù)據(jù)在所有視圖中正確地被渲染。使用 @EnvironmentObject 可以避免?@ObjectBinding 需要視圖層級之間傳遞數(shù)據(jù)的麻煩。

