【问题标题】:SwiftUI - PageView - Pass in different ViewsSwiftUI - PageView - 传入不同的视图
【发布时间】:2020-02-17 13:50:30
【问题描述】:

我通过线程在 SwiftUI 中成功实现了 PageView:
How to implement PageView in SwiftUI?

通过数组传递多个视图就像一个魅力,只要所有视图都具有相同的结构。
PageView([TestView(), TestView()]).
但是,我想传递不同的观点。
PageView([TestView(), AnotherView(), DifferentView()])

所有视图均为 SwiftUI 类型:
struct NAME : View { code }

当我尝试向数组添加不同的结构时,我收到以下错误消息:

var pageViewViewArray = [TestView(), AnotherView(), DifferentView()]

异构集合字面量只能推断为'[Any]'; 如果这是故意的,请添加显式类型注释。
插入'作为[任何]

通过将其转换为:

var pageViewViewArray = [TestView(), AnotherView(), DifferentView()] as! [Any]
PageView(pageViewViewArray)

PageView 会说:

协议类型 'Any' 不能符合 'View' 因为只有具体类型才能符合协议

我会非常感谢任何想法。

【问题讨论】:

    标签: swift uiview swiftui uipageviewcontroller


    【解决方案1】:

    通过将每个视图强制转换为 AnyView 来尝试使用类型擦除:

    var pageViewViewArray: [AnyView] = [AnyView(TestView()), AnyView(AnotherView()), AnyView(DifferentView())]
    

    文档here,以及使用它的示例here

    【讨论】:

    • 这完美地解决了问题,甚至包括文档和示例。谢谢!
    • 请记住 AnyView 很昂贵,因此仅在绝对必要时才使用它(在本例中是这样)。
    • @Peanutsmasher 这个解决方案将显着降低 SwiftUI 有效更新视图的能力。您可以(并且应该)避免这种情况。请看下面我的回答。
    【解决方案2】:

    有一种更有效的方法可以做到这一点,无需类型擦除。您应该创建一个通用视图,在其中注入一个枚举值,然后根据该值返回所需的视图。看看下面的例子:

    /// The first type of a view that accepts and shows a number
    struct ViewA: View {
    
        let number: Int
    
        var body: some View {
            Text("\(number)")
        }
    }
    
    /// The second type of a view that accepts and shows a string
    struct ViewB: View {
    
        let string: String
    
        var body: some View {
            Text(string)
        }
    }
    
    /// An enum type used for dependency injection
    enum ViewType {
    
        case viewA(number: Int)
        case viewB(string: String)
    }
    
    /// A common view which internally handles different kind of views based on injected enum value
    struct CommonView: View {
    
        let viewType: ViewType
    
        var body: some View {
            switch viewType {
            case .viewA(let number):
                ViewA(number: number)
    
            case .viewB(let string):
                ViewB(string: string)
            }
        }
    }
    
    // Views used for page view controller, that are now of same type:
    var pageViewViewArray = [
        CommonView(viewType: .viewA(number: 1)),
        CommonView(viewType: .viewB(string: "Hello, World!"))
    ]
    

    【讨论】:

    • 我不相信 SwiftUI 在我创建答案时允许 switch 或 if 语句返回,但这似乎有效!你介意告诉我为什么这个方法允许返回两种不同的 View 类型(ViewA 和 ViewB),从而绕过通常出现的“函数声明一个不透明的返回类型,但它的主体中的返回语句没有匹配的底层类型”如果我们尝试在 body 中有条件地返回两种不同的 View 类型?
    • 函数构建器中的 Switch 语句是 Swift 5.3 的一部分。它之所以有效,是因为主体实际上并没有返回两种不同的类型。它返回以下类型:_ConditionalContent<ViewA, ViewB>。您可以使用以下代码进行检查:let view = CommonView(viewType: .viewA(number: 1)); print(type(of: view.body))
    • 啊,明白了。那么为什么使用if 语句返回不同的Views 不起作用呢?我认为使用 if 语句也返回了 ConditionalContent?
    • 内部带有 if 语句的普通函数和 @ViewBuilder 函数之间存在差异。第一个返回两种不同的类型,因此代码不会编译。第二个返回_CondtionalContent
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-14
    • 2012-06-19
    • 2020-08-24
    • 2020-09-21
    • 1970-01-01
    • 2022-01-09
    相关资源
    最近更新 更多