首页 > 解决方案 > 将 VStack 作为 Struct 中的属性

问题描述

我需要VStack在这样的结构中拥有一个属性

struct Str
{ 
  let view: VStack
  ....
}

编译器说:
“对泛型 'VStack' 的引用需要在 <...>
Insert '<<#Content: View#>>'中使用参数

Xcodes“修复”产生:

struct Str
{
  let view: VStack<Content: View>
  ...
}

但编译器仍然抱怨:
“预期 '>' 来完成通用参数列表”

是否允许在结构中包含 VStack?

标签: swiftgenericsswiftui

解决方案


你发现那VStack不是真正的类型。我们可以更准确地将其描述为“类型构造函数”。它是这样声明的:

struct VStack<Content> where Content : View {
    ...
}

你给它一些其他类型作为它的Content参数,它给你一个类型。你给它 type Text,然后你得到 type VStack<Text>。你给它 type Image,然后你得到 type VStack<Image>。你回来的类型是不同的类型!类型的对象不是VStack<Text>类型的对象。VStack<Image>

所以你只需要知道要传递什么类型作为参数,对吧?好吧,问题在于许多SwiftUI “类型”实际上是泛型类型构造函数。所以类型变得复杂。这是我正在开发的应用程序的示例:

var body: some View {
    VStack(spacing: 0) {
        scene
        if store.model.latestError != nil {
            Divider()
            ErrorView(store: errorStore)
                .padding(20)
        }
    } //
        .frame(width: 450)
}

是什么类型的body?我可以在运行时打印它以找出:

print(type(of: AppView(store: store).body))

输出是

ModifiedContent<VStack<TupleView<(AnyView, Optional<TupleView<(Divider, ModifiedContent<ErrorView, _PaddingLayout>)>>)>>, _FrameLayout>

请注意,返回的对象类型body暴露了很多关于body' 实现的细节。例如,因为ErrorView有一个padding修饰符,所以body对象的类型包括ModifiedContent<ErrorView, _PaddingLayout>. 我可以在不更改类型的情况下将填充量从 20 更改为 30。但是,如果我完全删除padding修饰符,它会更改类型(通过更改ModifiedContent<ErrorView, _PaddingLayout>为 just ErrorView)。

那么你如何解决这个问题呢?一种方法是使用AnyView

struct Str {
    let view: AnyView
    ...
}

let str = Str(view: AnyView(VStack {
    ...
}))

请注意,有时您会听到应避免的建议AnyView。这是Joe Groff解释原因:

您可以使用 AnyView 包装器显式包装类型,它们可以动态更改。

但是,如果您可以将其尽可能向下推到视图图上,则动画系统将更容易。您可以拥有一种 Layout 类型,它采用.portrait/ .landscape、应用旋转、平移等以不同方式响应,然后过渡可以动画


推荐阅读