SwiftUI 4:在视图层次结构的不同级别上进行多个.navigationDestination()调用是否可行?

4

我想知道在视图层级的不同级别中多次调用navigationDestination()是否可行?我在谷歌上搜索了一下,但我找到的所有示例都是在NavigationStack中的顶级视图上调用它。我尝试了以下代码。它可以正常工作,但我不确定这是意外还是设计上的。我希望这样编码,因为它有助于组织代码。

struct ID<Value>: Hashable, Equatable {
    let uuid: UUID
    
    init() {
        uuid = UUID()
    }
}

typealias CategoryID = ID<Category>
typealias ItemID = ID<Item>

struct Catalog {
    var categories: [Category]
    
    func getCategory(_ categoryID: CategoryID) -> Category? {
        return categories.first { $0.id == categoryID }
    }
    
    func getItem(_ categoryID: CategoryID, _ itemID: ItemID) -> Item? {
        guard let category = getCategory(categoryID) else { return nil }
        return category.items.first { $0.id == itemID }
    }
}

struct Category: Identifiable {
    var id: CategoryID = .init()
    var name: String
    var items: [Item]
}

struct Item: Identifiable {
    var id: ItemID = .init()
    var name: String
}

func createTestData() -> Catalog {
    let categories = [
        Category(name: "Category A", items: [Item(name: "Item a1"), Item(name: "Item a2")]),
        Category(name: "Category B", items: [Item(name: "Item b1"), Item(name: "Item b2")])
    ]
    return Catalog(categories: categories)
}

struct ContentView: View {
    @State var catalog: Catalog = createTestData()
    
    var body: some View {
        NavigationStack {
            List {
                ForEach(catalog.categories) { category in
                    NavigationLink(category.name, value: category.id)
                }
            }
            // This is the typical place to make one or multiple calls of navigationDestination().
            .navigationDestination(for: CategoryID.self) { categoryID in
                CategoryView(categoryID: categoryID, catalog: $catalog)
            }
        }
    }
}

struct CategoryView: View {
    let categoryID: CategoryID
    @Binding var catalog: Catalog
    
    var body: some View {
        if let category = catalog.getCategory(categoryID) {
            List {
                ForEach(category.items) { item in
                    NavigationLink(item.name, value: item.id)
                }
            }
            // Q: is it OK to call navigationDestination() here?
            .navigationDestination(for: ItemID.self) { itemID in
                ItemView(categoryID: categoryID, itemID: itemID, catalog: $catalog)

            }
        }
    }
}

struct ItemView: View {
    let categoryID: CategoryID
    let itemID: ItemID
    @Binding var catalog: Catalog

    var body: some View {
        if let item = catalog.getItem(categoryID, itemID) {
            Text(item.name)
        }
    }
}

注意代码使用了通用标识符类型,否则SwifUI无法区分navigationDestination(for: CategoryID)navigationDestination(for: ItemID)

1
是的,非常好 :) ... 假设它们都是不同类型的,我认为相同类型的重复将被忽略。 - Asperi
谢谢,@Asperi。你有这些信息的来源或参考资料吗?还是基于你的实验和理解? - rayx
这个本来应该没问题,但是从 beta 3 开始它不能使用相同的 ID 类型,希望他们能修复这个问题。通常这种偏好修改器会经过一个 reducer 处理,所以这个有点奇怪。 - malhal
1
@malhal 注意文档中的这句话:“如果需要呈现多种数据,可以将多个导航目标修饰符添加到堆栈中。”我认为它建议同一类型的函数只应调用一次。 - rayx
1个回答

3
在接口合同文档中,它是正确的(“to the stack”,即整个堆栈,堆栈中的任何位置,除了下面所述的“lazy”): demo

谢谢。在我发问题之前,我已经查看了文档并看到了这一点。在我看来,描述有歧义。但知道其他人如何解释它会有所帮助 :) - rayx
一切未被明确禁止的都是允许的。即使有些被禁止的事情也有例外。总的来说 - 公共API中的一切都是允许的。 - Asperi

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