繁体   English   中英

SwiftUI 中不同视图的数组可在列表中作为 NavigationLink 进行迭代

[英]Array of different Views in SwiftUI iterable in a list as NavigationLink

我正在制作一个具有各种屏幕的应用程序,我想在一组视图中收集所有符合View的屏幕。 在应用程序的主屏幕上,我想向所有屏幕标题显示带有NavigationLink的列表。 我遇到的问题是,如果我尝试创建一个自定义视图结构并拥有一个初始化给给定屏幕并返回它的属性。 我一直遇到问题,编译器迫使我更改变量以接受any View而不仅仅是View或删除的类型some View

struct Screen: View, Identifiable {
    var id: String {
        return title
    }
    
    let title: String
    let destination: any View
    
    var body: some View {
        NavigationLink(destination: destination) { // Type 'any View' cannot conform to 'View'
            Text(title)
        }
    }
}

这有效:

struct Screen<Content: View>: View, Identifiable {
    var id: String {
        return title
    }
    
    let title: String
    let destination: Content
    
    var body: some View {
        NavigationLink(destination: destination) {
            Text(title)
        }
    }
}

但是这种方法的问题是我不能将不同的屏幕放入一个数组中,可以按如下方式修复:

struct AllScreens {
    var screens: [any View] = []
    
    init(){
        let testScreen = Screen(title: "Charties", destination: SwiftChartsScreen())
        let testScreen2 = Screen(title: "Test", destination: NavStackScreen())
        screens = [testScreen, testScreen2]
    }
}

但是当我尝试访问列表中的屏幕时,它无法在没有类型转换的情况下推断出它是什么视图。 我试图实现的最终结果是能够传入一系列屏幕并将其标题显示在如下列表中。 截屏

目前我只能通过硬编码列表元素来实现这一点,这很有效。

import SwiftUI

struct MainScreen: View {
    let screens = AppScreens.allCases
    var body: some View {
        NavigationStack() {
            List() {
                AppScreens.chartsScreen
                AppScreens.navStackScreen
            }
            .navigationTitle("WWDC 22")
        }
    }
}

SwiftUI 是数据驱动的响应式框架,而 Swift 是严格类型的语言,因此我们可以让数据负责提供相应的视图(现在与ViewBuilder 的帮助非常简单)。

所以这里有一个方法。 使用 Xcode 13+ / iOS 15+ 测试(NavigationView 或 NavigationStack - 这并不重要)

enum AllScreens: CaseIterable, Identifiable {   // << type !!

    case charts, navStack   // << known variants

    var id: Self { self }

    @ViewBuilder var view: some View { // corresponding view !!
        switch self {
        case .charts:
            Screen(title: "Charties", destination: SwiftChartsScreen())
        case .navStack:
            Screen(title: "Test", destination: NavStackScreen())
        }
    }
}

用法很明显:

struct MainScreen: View {
    var body: some View {
        NavigationStack { // or `NavigationView` for backward compatibility
            List(AllScreens.allCases) {
                $0.view                 // << data knows its presenter
            }
            .navigationTitle("WWDC 22")
        }
    }
}

GitHub 上的测试模块

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM