首页 > 解决方案 > 关于 SwiftUI 中 View 作为类型约束的问题

问题描述

我正在尝试创建一个名为 Lesson 的类。每个课程对象应包含不同的课程页面(视图),以便每个课程卡片可以导航到不同的页面。

课程课程的代码如下:

class Lesson<T: View>: Identifiable {
    let id = UUID()
    var lessonName: String
    var lessonPage: T
    
    init(lessonName: String,
         lessonPage: T = DefaultLessonPageView() as! T) {
        self.lessonName = lessonName
        self.lessonPage = lessonPage
    }
}

问题是编译以下代码时发生错误:

struct ButtonPageView: View {
    var lesson: Lesson<View>

    var body: some View {
        NavigationLink(destination: lesson.lessonPage()) {
            some view here
        }
    }
}

错误如下:

协议类型“View”的值不能符合“View”;只有结构/枚举/类类型可以符合协议

我应该怎么做才能解决这个问题?

标签: swiftgenericsswiftui

解决方案


对于泛型,我们应该记住类型是在编译类型中推断出来的,因此在编写表达式时应该知道视图。因此 SwiftUI 为此使用了构建器模式。

这是一个演示如何通过支持通用页面和提供的一些默认页面来解决它。

使用 Xcode 12.4 / iOS 14.4 测试

class Lesson<T: View>: Identifiable {
    let id = UUID()
    var lessonName: String

    var builder: (() -> T)?    // << optional builder
    
    @ViewBuilder
    func lessonPage() -> some View {   // ViewBuilder gives possibility
        if let page = builder?() {     // to provide default view
            page
        } else {
            DefaultLessonPageView()
        }
    }
    
    init(lessonName: String,
         lessonPage: (() -> T)? = nil) {  // by default no view
        self.lessonName = lessonName
        self.builder = lessonPage
    }
}

struct ButtonPageView<Page: View>: View {
    var lesson: Lesson<Page>

    var body: some View {
        NavigationLink(destination: lesson.lessonPage()) {
            Text("")
        }
    }
}

现在我们可以将它们用作

var body: some View {
    // no view, so let compiler know
    ButtonPageView<Never>(lesson: Lesson(lessonName: "First"))

    // page type is inferred from builder
    ButtonPageView(lesson: Lesson(lessonName: "Second", lessonPage: {
        Text("Second Lesson")
    }))
}

推荐阅读