為什麼SwiftUI使用 some View 作為視圖類型?

SwiftUI非常依賴於Swift的一個叫做「不透明返回類型(opaque return types)」的強大特性,您每次編寫some View時都可以看到它的實際作用。它意味著「一種符合View協議的特定類型,但我們不想說它到底是什麼。」

返回some View與僅返回View相比有兩個重要區別:

1、我們必須始終返回相同類型的視圖。 2、即使我們不知道返回的視圖類型,編譯器也同樣不知道。

第一個區別對於性能很重要:SwiftUI需要能夠查看我們顯示的視圖並理解它們是如何變化的,這樣它才能正確地更新用戶介面。如果允許我們隨機更改視圖,那麼對於SwiftUI來說,很難準確地找出更改的內容——它需要放棄一切,在每次小的更改之後重新開始。

第二個區別很重要,因為SwiftUI使用ModifiedContent構建數據的方式。之前我給你看了這段程式碼:

Button("Hello World") {      print(type(of: self.body))  }  .frame(width: 200, height: 200)  .background(Color.red)

這將創建一個簡單的按鈕,然後使其列印其確切的Swift類型,並提供一些帶有幾個 ModifiedContent實例的長輸出。

View協議有一個關聯的類型附加到它之上,這是Swift說View本身並不意味著什麼的方式,我們需要確切地說它是什麼類型的視圖。它實際上是容器,就像Swift不讓我們說「這個變數是數組」一樣,而是要求我們說數組中的內容:「這個變數是字元串數組。(個人理解:一個數組只能是[String]或者[Any]或者其他,但是不能是[],同理這裡的View也是一樣)

所以,它不允許寫這樣的視圖:

struct ContentView: View {      var body: View {          Text("Hello World")      }  }

但寫這樣的視圖是完全合法的:

struct ContentView: View {      var body: Text {          Text("Hello World")      }  }

返回View是沒有意義的,因為Swift想知道視圖中的內容——它是一個必須填滿的容器。另一方面,返回Text是可以的,因為我們已經填充了這個容器,Swift知道視圖是什麼。

現在讓我們回到前面的程式碼:

Button("Hello World") {      print(type(of: self.body))  }  .frame(width: 200, height: 200)  .background(Color.red)

如果我們想從我們的body屬性中返回其中一個,我們應該寫些什麼?雖然您可以嘗試找出修ModifiedContent泛型的確切組合,但這是非常痛苦的,而簡單的事實是,我們不在乎:這都是SwiftUI內部的東西。

some View讓我們做的是說「這將返回一種特定類型的視圖,如ButtonText,但我不想說它到底什麼。」因此,視圖容器將由一個真實的視圖填充,但我們不需要寫出確切的類型。

想進一步嗎?

現在,如果你好奇的話,你可能會想,SwiftUI是如何處理VStack這樣的東西的——它符合View協議,但是「它填充了什麼樣的內容?」,「容器裡面能裝很多不同的東西?

好吧,如果您創建了一個包含兩個文本視圖的VStack,那麼SwiftUI會無聲地創建一個TupleView來包含這兩個視圖——一種特殊類型的視圖,其中正好包含兩個視圖。所以,「VStack填充了什麼樣的視圖?」回答:「這是一個包含兩個文本視圖的TupleView。」

如果VStack中有三個文本視圖呢?然後是一個包含三個視圖的TupleView。或者四種觀點。或者八個視圖,甚至十個視圖——TupleView的一個版本可以跟蹤十種不同的內容:

TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)>

這就是為什麼SwiftUI在一個父級中不允許超過10個視圖的原因:他們編寫了TupleView的版本,可以處理2到10個視圖,但不能超過10個(另一個版本:ForEach使用的)。

Previous: 為什麼SwiftUI的修飾符順序很重要?

Hacking with iOS: SwiftUI Edition

Next: 條件修飾符

賞我一個贊吧~~~