为什么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: 条件修饰符

赏我一个赞吧~~~