使用SwiftUI展开元组视图

5

好的,SwiftUI在本周发布,所以我们都是新手但是...我有以下测试代码:

var body: some View {
    switch shape {
    case .oneCircle:
        return ZStack {
            Circle().fill(Color.red)
        }
    case .twoCircles:
        return ZStack {
            Circle().fill(Color.green)
            Circle().fill(Color.blue)
        }
    }
}

产生以下错误:

函数声明了一个不透明的返回类型,但其主体中的返回语句没有匹配的基础类型

出现这种情况是因为第一个ZStack的类型是:

ZStack<ShapeView<Circle, Color>>

第二种类型如下:
ZStack<TupleView<(ShapeView<Circle, Color>, ShapeView<Circle, Color>)>>

我该如何在SwiftUI中处理这个问题?它们可以被扁平化或者被转换为同一种类型吗?

3个回答

13

解决这个问题的一种方法是使用类型擦除器AnyView

var body: some View {
    switch shape {
    case .oneCircle:
        return AnyView(ZStack {
            Circle().fill(Color.red)
        })
    case .twoCircles:
        return AnyView(ZStack {
            Circle().fill(Color.green)
            Circle().fill(Color.blue)
        })
    }
}

更新

我添加以下内容来回答那些问为什么需要这个的评论者们。

一个评论者说:

ZStack仍然是一个视图,对吗?

实际上,不是的。 ZStack本身不是一个ViewZStack<SomeConcreteView> 才是一个View

ZStack的声明看起来像这样:

public struct ZStack<Content> : View where Content : View

ZStack是泛型的。这意味着ZStack本身不是一种类型,而是一个“类型构造器”。

Swift社区通常不会讨论类型构造器的概念。类型构造器本质上是在编译时运行的函数。该函数将一个或多个类型作为参数,并返回一个类型。

ZStack是一个接受一个参数的类型构造器。如果您反复使用不同的参数'调用'ZStack,它将返回不同的答案。这就是Robert Gummesson在他的问题中展示的内容:

这是因为第一个ZStack是这种类型:

ZStack<ShapeView<Circle, Color>>

第二种类型如下:

ZStack<TupleView<(ShapeView<Circle, Color>, ShapeView<Circle, Color>)>>
在第一个案例中,程序使用参数ShapeView<Circle, Color>‘调用’ ZStack, 并得到了一个类型作为答案。在第二个案例中,程序使用不同的参数TupleView<(ShapeView<Circle,Color>, ShapeView<Circle,Color>)> ‘调用’ ZStack,因此它得到了一个不同的类型作为答案。
声明var body: some View 表示body方法返回一个特定的具体类型(由编译器推断)并符合View协议。由于对ZStack的两次‘调用’返回不同的具体类型,我们必须找到一种方法将它们都转换为单一的公共类型。这就是AnyView的目的。请注意,AnyView不是泛型,也就是说,它不是一个类型构造器。它只是一个普通类型。

1
这个答案“可行”,但有没有人能够发表意见,确认它是否正确,如果正确的话,为什么会有效呢? - Alan Zeino
我和楼主遇到了同样的错误,但我不明白为什么需要这样做。ZStack 仍然是一个 View,对吧?而且 body 属性的类型是一些 View...所以,只要返回一个 View 就应该可以工作。 - NightFuryLxD

1
你也可以使用 Group,它是一个逻辑容器,不会改变任何可视化内容。
 var body: some View {
    Group {
     switch shape {
     case .oneCircle:
        return ZStack {
            Circle().fill(Color.red)
        }
     case .twoCircles:
        return ZStack {
            Circle().fill(Color.green)
            Circle().fill(Color.blue)
        }
     }
    }
}

0

我知道这是一个老问题。但我偶然发现了它,现在正确的方法是使用@ViewBuilder注释(注意缺少return):

    @ViewBuilder var body: some View {
        switch shape {
        case .oneCircle:
            ZStack {
                Circle().fill(Color.red)
            }
        case .twoCircles:
            ZStack {
                Circle().fill(Color.green)
                Circle().fill(Color.blue)
            }
        }
    }

在这种特定情况下,将ZStack因素分离出来:
    var body: some View {
        ZStack {
            switch shape {
            case .oneCircle:
                Circle().fill(Color.red)
            case .twoCircles:
                Circle().fill(Color.green)
                Circle().fill(Color.blue)
            }
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接