為什麼SwiftUI修飾符順序很重要?
- 2020 年 3 月 27 日
- 筆記
每當我們將修飾符應用於SwiftUI視圖時,我們實際上都會創建一個應用了更改的新視圖——我們不僅會修改現有的視圖。如果您考慮一下,這種行為是有道理的——我們的視圖僅保留我們賦予它們的確切屬性,因此,如果我們設置背景顏色或字體大小,則無處存儲該數據。
我們將在下一章中查看為什麼會發生這種情況,但是首先,我想看看這種行為的實際含義。看一下這段代碼:
Button("Hello World") { // do nothing } .background(Color.red) .frame(width: 200, height: 200)
您認為它運行時會是什麼樣?
您很可能猜錯了:您不會在中間看到帶有「 Hello World」的200×200紅色按鈕。相反,您會看到一個200×200的空正方形,中間是「 Hello World」,在「 Hello World」周圍有一個紅色矩形。
如果思考一下修飾符的工作原理,您就可以了解為什麼會如此:每個修飾符都會創建一個應用了該修飾符的新結構體,而不是在視圖上設置屬性。
您可以通過查詢視圖主體的類型來窺視SwiftUI的底層。將按鈕修改為如下:
Button("Hello World") { print(type(of: self.body)) } .background(Color.red) .frame(width: 200, height: 200)
Swift的type(of:)
方法會打印特定值的確切類型,在這種情況下,它將打印以下內容:ModifiedContent<ModifiedContent<Button<Text>, _BackgroundModifier<Color>>, _FrameLayout>
您可以在這裡看到兩件事:
- 每次我們修改視圖時,SwiftUI都會使用以下泛型來應用該修飾符:
ModifiedContent<OurThing, OurModifier>
- 當我們應用多個修飾符時,它們會疊加在一起:
ModifiedContent<ModifiedContent<…
要了解該類型是什麼,請從最裏面的類型開始,然後逐步解決:
- 最裏面的類型是
ModifiedContent<Button<Text>, _BackgroundModifier<Color>
:您的按鈕上有一些帶有背景色的文本。 - 在外部,我們有了
ModifiedContent<…, _FrameLayout>
,它使用了我們的第一個視圖(按鈕+背景色),並為其提供了Frame。
如您所見,我們使用ModifiedContent
類型堆疊——每個視圖都需要一個視圖進行轉換以及要進行的實際更改,而不是直接修改視圖。
這意味着修飾符的順序很重要。如果我們重寫代碼以在設置Frame後應用背景色,那麼您就會得到預期的結果:
Button("Hello World") { print(type(of: self.body)) } .frame(width: 200, height: 200) .background(Color.red)
現在最好的思考方法是,想像一下SwiftUI在每個修飾符之後都會呈現您的視圖。因此,只要您說 .background(Color.red)
,它就會將背景顏色變為紅色,而不管您給它什麼Frame。如果您之後再擴展Frame,它將不會神奇地重繪已經應用了的背景。
使用修飾符的一個重要副作用是,我們可以多次應用相同的效果:每個修飾符都會簡單地添加到以前的內容中。
例如,SwiftUI為我們提供了padding()
修飾符,該修飾符在視圖周圍添加了一些空間,從而不會將其推到其他視圖或屏幕邊緣。如果我們應用填充,然後應用背景色,然後應用更多填充和不同的背景色,則可以為視圖提供多個邊框,如下所示:
Text("Hello World") .padding() .background(Color.red) .padding() .background(Color.blue) .padding() .background(Color.green) .padding() .background(Color.yellow)