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

Posted

技术标签:

【中文标题】Text 或 Image 等视图类型如何符合 SwiftUI 中的 View 协议?【英文标题】:How do view types like Text or Image conform to the View protocol in SwiftUI? 【发布时间】:2019-06-06 03:40:26 【问题描述】:

SwiftUI 中的视图类型让我很困惑:

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

Text 类型为例。定义如下:

public struct Text : Equatable  ... 

我找不到任何增加符合View 协议的公共扩展,例如

extension Text: View  ... 

official documentation 中的 Relationships 部分简单说明:

符合: 平等

仅此而已。

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

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

如果Text 不符合View,这将不可能并引发编译器错误。

some View 是一种不透明的结果类型,这意味着它是具有标识的特定类型,但符合 View。)

那么这怎么可能呢?

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

【问题讨论】:

【参考方案1】:

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

无论如何,这就是理解为什么 Text 似乎不符合 View 的秘诀:Xcode 和 Apple 的文档生成器有意省略了 SDK 中以 _ 开头的标识符。

因此,如果您想查看一个类型的完整公共声明,您不能依赖 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 通过与 _UnaryView 的一致性间接符合 View。由于_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

【讨论】:

出色的中肯答案。感谢您解决这个看似矛盾的问题!【参考方案2】:

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

    原始视图:TextImageCircle 等 容器视图: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

【讨论】:

是的,虽然这可能是真的,但它不能回答指定View 一致性的问题。这是在编译时发生的,而不是在运行时。否则代码将无法编译。 实际上,您在复制的扩展中缺少 View 一致性。扩展文本:视图就是它所说的。它通过扩展符合就是答案。它就在标题中。【参考方案3】:

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

Image 类型具有直接符合View 的扩展:

extension Image : View 

Circle符合Shape,它本身也符合View

public struct Circle : Shape 
  ...


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

【讨论】:

"SwiftUI 视图类型符合 View 协议" → 这是我提出问题的假设。但是在哪里定义了一致性?对于Circle,您可以回答这个问题,但是例如TextHStack 呢?【参考方案4】:

扩展。

   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

【讨论】:

以上是关于Text 或 Image 等视图类型如何符合 SwiftUI 中的 View 协议?的主要内容,如果未能解决你的问题,请参考以下文章

如何解决 不能以 DISTINCT 方式选择 textntext 或 image 数据类型

delphi程序中使用union all 查询sql数据中的两个表报"不能比较或排序 text、ntext和image类型"的错误。

“类型‘()’不能符合‘视图’;只有结构/枚举/类类型可以符合协议”

是否有“或”属性选择器,如输入[type = button | text]?

.SQL Server中 image类型数据的比较

如何在 SwiftUI 中设置视图中的控制器