為什麼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)