Text 或 Image 等视图类型如何符合 SwiftUI 中的 View 协议?

How do view types like Text or Image conform to the View protocol in SwiftUI?

关于 SwiftUI 中的视图类型,有一件大事让我感到困惑:

它们似乎不符合 View 协议,但不知何故,它们神秘地符合。

Text类型为例。定义如下:

public struct Text : Equatable { ... }

我找不到任何 public 扩展符合 View 协议,例如

extension Text: View { ... }

official documentation 中的 Relationships 部分简单地指出:

Conforms To: Equatable

仅此而已。

然而,我可以 return 一个 Text 的实例,其中需要一些 View,例如:

var body: some View {
    Text("I'm a View, I swear!") 
}

如果 Text 不符合 View,这是不可能的,并且会抛出编译器错误。

(some View 是一个 不透明的结果类型 这意味着它是一种具有身份的特定类型,但符合 View.)

这怎么可能?

SwiftUI 视图类型指定的 View 协议一致性在哪里(如 TextImageCircle、 ...)?

如您所知,有 2 种类型是视图..

  1. 原始视图:TextImageCircle
  2. 容器视图:ListHStackVStack

也就是说,下面是 Text 的扩展,Body 设置为 Never,这意味着它不允许有 body,因为它是一个原始视图,用于结束 body 循环。

因此,(根据我的理解)SwiftUI 在运行时发现原始视图不在容器视图内时将 Text 包装在容器视图内。

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension Text {

    /// The type of view representing the body of this view.
    ///
    /// When you create a custom view, Swift infers this type from your
    /// implementation of the required `body` property.
    public typealias Body = Never
}

SwiftUI 视图类型符合 View 协议,否则如您所述,代码将无法编译。我发现其中一些在 SwiftUI public 扩展中可用:

Image 类型有一个直接符合 View:

的扩展
extension Image : View {
}

Circle 符合 Shape,后者本身符合 View:

public struct Circle : Shape {
  ...
}

public protocol Shape : Equatable, Animatable, View {
  ...
}

扩展。

   extension Text : View {

    /// The type of view representing the body of this view.
    ///
    /// When you create a custom view, Swift infers this type from your
    /// implementation of the required `body` property.
    public typealias Body = Never
}

这个问题是在 2019 年 6 月 6 日 WWDC 期间提出的,当时我们只有 Xcode 11 和 SwiftUI 的第一个测试版。所以正确回答这个问题需要访问那个版本的 SwiftUI。你可以 download Xcode 11 beta 1 here. (Thank you, xcodereleases.com!) 不过,你正在尝试解压缩存档,因为(我认为)它是用一个已经过期的证书签名的。我使出了黑魔法(单步执行LLDB中的xip命令,关键时刻修改内存,颠覆证书验证)。您可以在解包之前将系统时间设置回 2019 年 6 月 6 日。

无论如何,这是理解为什么 Text 似乎不符合 View 的秘诀:Xcode,以及 Apple 的文档生成器,故意省略 SDK 中的标识符_.

因此,如果您想查看类型的完整 public 声明,您不能依赖 Xcode 或文档来向您展示。相反,您必须为模块挖掘 .swiftinterface 文件。对于 SwiftUI,你可以在这里找到它,相对于 Xcode.app 目录:

Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/SwiftUI.framework/Modules/SwiftUI.swiftmodule/arm64.swiftinterface

在该文件的 Xcode 11 beta 1 版本中,您找不到直接的一致性 Text: View。相反,您会发现:

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension Text : _UnaryView {
  public static func _makeView(view: _GraphValue<Text>, inputs: _ViewInputs) -> _ViewOutputs
  public typealias Body = Swift.Never
}

你会发现_UnaryViewView的一个子协议:

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol _UnaryView : SwiftUI.View where Self.Body : SwiftUI._UnaryView {
}

因此,在 Xcode 11 beta 1 和相应的 iOS、macOS、tvOS 和 watchOS beta 中,Text 通过其一致性间接符合 View_UnaryView。由于 _UnaryView 是 SDK 的一部分并且以 _ 开头,Xcode 并且 Apple 文档隐藏了该符号。所以通过正常的方法是看不到一致性的。

在后来的某个时候(但我相信,在 Xcode 11.0 测试版期间),Apple 取消了 _UnaryView 协议并使 Text 直接符合 View.因此,如果您检查 Xcode 11.4(我写这篇文章时的当前版本)中的 SwiftUI .swiftinterface 文件,您会发现:

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension Text : SwiftUI.View {
  public static func _makeView(view: SwiftUI._GraphValue<SwiftUI.Text>, inputs: SwiftUI._ViewInputs) -> SwiftUI._ViewOutputs
  public typealias Body = Swift.Never
}