SwiftUI 中的内容是什么? [英] What is Content in SwiftUI?

查看:37
本文介绍了SwiftUI 中的内容是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在文档中,我在不同的上下文中看到了 Content:

////一个修饰符,可以应用于一个视图或其他视图修饰符,///生成原始值的不同版本.@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)公共协议 ViewModifier {///传递给 `body()` 的内容视图类型.类型别名内容}

这里

////将其子项排列在垂直线上的视图.@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)公共结构 VStack其中内容:查看{

我无法在文档中找到对 Content 含义的正确解释.SwiftUI 中是否有任何预定义的 content 用法?

解决方案

重要的是要了解 SwiftUI 大量 使用泛型类型.在 SwiftUI(和 Combine)发布之前,我从未见过任何 Swift 代码如此大量地使用泛型.SwiftUI 中几乎所有符合 View 的类型(和符合 ViewModifier 的类型)都是泛型类型.

ViewModifier

那么,首先让我们谈谈ViewModifier.ViewModifier 是一个协议.其他类型可以符合ViewModifier,但没有任何变量或值只能具有普通类型ViewModifier.

为了使类型符合 ViewModifier,我们定义了一个 body 方法,该方法接受一个 Content(无论是什么)并返回一个 Body(不管是什么):

func body(content: Content) ->身体

ViewModifier 本质上就是这种方法,它以 Content 作为输入并返回一个 Body 作为输出.

什么是Body?ViewModifier 将其定义为带有约束的 associatedtype:

 associatedtype Body : 查看

这意味着我们可以在 ViewModifier 中选择称为 Body 的特定类型,并且我们可以为 Body 选择任何类型因为它符合 View 协议.

Content 是什么?文档告诉你它是一个 typealias,这意味着我们可能无法选择它是什么.但是文档并没有告诉你 Content 是什么的别名,所以我们不知道 body 可以用 Content 做什么> 收到了!

文档没有告诉您的原因是因为 Xcode 被编程为如果符号以下划线 (_) 开头,则不会向您显示来自 SDK 的公共符号.但是您可以通过查看 SwiftUI 的 .swiftinterface 文件来查看 ViewModifier 的真实定义,包括隐藏的符号.我在这个答案中解释了如何找到该文件.

查阅那个文件,我们找到了ViewModifier的真正定义:

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)公共协议 ViewModifier {static func _makeView(modifier: SwiftUI._GraphValue, 输入: SwiftUI._ViewInputs, body: @escaping (SwiftUI._Graph, SwiftUI._ViewInputs) -> SwiftUI._ViewOutputs) ->SwiftUI._ViewOutputsstatic func _makeViewList(modifier: SwiftUI._GraphValue, 输入: SwiftUI._ViewListInputs, body: @escaping (SwiftUI._Graph, SwiftUI._ViewListInputs) -> SwiftUI._ViewListOutputs) ->SwiftUI._ViewListOutputs关联类型主体:SwiftUI.Viewfunc body(content: Self.Content) ->自体typealias Content = SwiftUI._ViewModifier_Content}

还有一些对 ViewModifier 的扩展定义了 body_makeView_makeViewList 的默认值,但是我们可以忽略这些.

所以无论如何,我们可以看到Content_ViewModifier_Content的别名,它是一个没有定义的struct任何有趣的公共接口,但(在扩展中)符合 View.所以这告诉我们,当我们编写自己的 ViewModifier 时,我们的 body 方法将接收某种 View(具体类型由框架,我们可以将其称为 Content),并返回某种 View(我们可以选择特定的返回类型).

所以这是一个示例 ViewModifier,我们可以将其应用于任何 View.它填充修改后的视图并为其提供彩色背景:

struct MyModifier: ViewModifier {var 颜色:颜色func 正文(内容:内容)->一些视图{返回 content.padding().background(color)}}

请注意,我们不必为 body 返回的 View 类型命名.我们可以使用 some View 并让 Swift 推断出具体的类型.

我们可以这样使用它:

Text("Hello").modifier(MyModifier(color: .red))

VStack

现在让我们谈谈 VStack.VStack 类型是一个 struct,而不是协议.它是泛型的,这意味着它接受类型参数(就像函数接受函数参数一样).VStack 接受一个类型参数,名为 Content.这意味着 VStack 定义了一个 family 类型,一个对应于它允许 Content 的每种类型.

由于VStackContent参数被约束为符合View,这意味着对于每个View-符合类型,有对应的VStack类型.对于Text(符合View),有VStack.对于Image,有VStack.对于Color,有VStack.

但我们通常不会拼出我们正在使用的 VStack 的完整类型实例,而且我们通常不会将 Content 类型设为像这样的原始类型TextImage.使用 VStack 的全部原因是在一列中排列多个视图.VStack 的使用告诉 Swift 垂直排列它的子视图,VStackContent 类型参数指定子视图的类型.>

例如,当你这样写时:

VStack {文本(你好")按钮(动作:{}){文本(点击我!")}}

您实际上是在创建这种类型的实例:

VStack)>>

这里的Content类型参数是类型TupleView<(Text, Button)>,它本身就是一个泛型类型TupleView 有自己的类型参数 TT 这里是 (Text, Button)(一个 2 元组,也称为一对).所以类型的 VStack 部分告诉 SwiftUI 垂直排列子视图,而 TupleView<(Text, Button)> 部分告诉 SwiftUI 有两个子视图:一个 Text 和一个 Button.

您可以看到即使是这个简短的示例也是如何生成具有多层嵌套泛型参数的类型.所以我们肯定想让编译器为我们找出这些类型.这就是 Apple 在 Swift 中添加 some View 语法的原因——这样我们就可以让编译器找出确切的类型.

In the documentation, I see Content in a different context:

/// A modifier that can be applied to a view or other view modifier,
/// producing a different version of the original value.
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol ViewModifier {
    /// The content view type passed to `body()`.
    typealias Content
}

and here

/// A view that arranges its children in a vertical line.
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
public struct VStack<Content> where Content : View {

I can't find in the documentation the proper explanation of what does Content mean. Is there any predefined content usage in SwiftUI?

解决方案

It's important to understand that SwiftUI makes heavy use of generic types. Before the release of SwiftUI (and Combine), I had never seen any Swift code that makes such heavy use of generics. Almost all of the View-conforming types (and ViewModifier-conforming types) in SwiftUI are generic types.

ViewModifier

So, first let's talk about ViewModifier. ViewModifier is a protocol. Other types can conform to ViewModifier, but no variable or value can just have the plain type ViewModifier.

To make a type conform to ViewModifier, we define a body method that takes a Content (whatever that is) and returns a Body (whatever that is):

func body(content: Content) -> Body

A ViewModifier is essentially just this one method, that takes a Content as input and returns a Body as output.

What's Body? ViewModifier defines it as an associatedtype with a constraint:

associatedtype Body : View

This means we get to pick the specific type known as Body in our ViewModifier, and we can pick any type for Body as long as it conforms to the View protocol.

Any what is Content? The documentation tells you it's a typealias, which means we probably don't get to pick what it is. But the documentation doesn't tell you what Content is an alias of, so we don't know anything about what body can do with the Content it receives!

The reason the documentation doesn't tell you is because Xcode is programmed not to show you a public symbol from the SDK if the symbol begins with an underscore (_). But you can see the true definition of ViewModifier, including the hidden symbols, by looking in the .swiftinterface file for SwiftUI. I explain how to find that file in this answer.

Consulting that file, we find the true definition of ViewModifier:

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol ViewModifier {
  static func _makeView(modifier: SwiftUI._GraphValue<Self>, inputs: SwiftUI._ViewInputs, body: @escaping (SwiftUI._Graph, SwiftUI._ViewInputs) -> SwiftUI._ViewOutputs) -> SwiftUI._ViewOutputs
  static func _makeViewList(modifier: SwiftUI._GraphValue<Self>, inputs: SwiftUI._ViewListInputs, body: @escaping (SwiftUI._Graph, SwiftUI._ViewListInputs) -> SwiftUI._ViewListOutputs) -> SwiftUI._ViewListOutputs
  associatedtype Body : SwiftUI.View
  func body(content: Self.Content) -> Self.Body
  typealias Content = SwiftUI._ViewModifier_Content<Self>
}

There are also some extensions to ViewModifier that define defaults for body, _makeView, and _makeViewList, but we can ignore those.

So anyway, we can see that Content is an alias for _ViewModifier_Content<Self>, which is a struct that doesn't define any interesting public interface, but does (in an extension) conform to View. So this tells us that, when we write our own ViewModifier, our body method will receive some sort of View (the specific type is defined by the framework and we can just call it Content), and return some sort of View (we get to pick the specific return type).

So here's an example ViewModifier that we can apply to any View. It pads the modified view and gives it a colored background:

struct MyModifier: ViewModifier {
    var color: Color

    func body(content: Content) -> some View {
        return content.padding().background(color)
    }
}

Note that we don't have to name the type of View returned by body. We can use some View and let Swift deduce the specific type.

We can use it like this:

Text("Hello").modifier(MyModifier(color: .red))

VStack

Now let's talk about VStack. The VStack type is a struct, not a protocol. It is generic, which means it takes type parameters (just like a function takes function parameters). VStack takes a single type parameter, named Content. This means VStack defines a family of types, one for every type it allows for Content.

Since VStack's Content parameter is constrained to conform to View, this means that for every View-conforming type, there is a corresponding VStack type. For Text (which conforms to View), there is VStack<Text>. For Image, there is VStack<Image>. For Color, there is VStack<Color>.

But we don't normally spell out the full type instance of VStack we're using, and we don't usually have the Content type be a primitive like Text or Image. The whole reason to use a VStack is to arrange multiple views in a column. The use of VStack tells Swift to arrange its subviews vertically, and the VStack's Content type parameter specifies the types of the subviews.

For example, when you write this:

VStack {
    Text("Hello")
    Button(action: {}) {
        Text("Tap Me!")
    }
}

you're actually creating an instance of this type:

VStack<TupleView<(Text, Button<Text>)>>

The Content type parameter here is the type TupleView<(Text, Button<Text>)>, which is itself a generic type TupleView with its own type parameter named T, and T here is (Text, Button<Text>) (a 2-tuple, also called a pair). So the VStack part of the type tells SwiftUI to arrange the subviews vertically, and the TupleView<(Text, Button<Text>)> part tells SwiftUI that there are two subviews: a Text and a Button<Text>.

You can see how even this short example generates a type with several levels of nested generic parameters. So we definitely want to let the compiler figure out these types for us. This is why Apple added the some View syntax to Swift—so we can let the compiler figure out the exact type.

这篇关于SwiftUI 中的内容是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆