SwiftUI 的 List 如何更新选择绑定?

ser*_*gpa 4 swift swiftui

我真的很难理解这是如何List(selection:)导致选择绑定更新的。

我正在https://developer.apple.com/documentation/swiftui/building_a_great_mac_app_with_swiftui上取消选择 Apple 示例代码

该项目是Session2/Part2-End/GardenApp.xcodeproj

左侧有一个侧边栏,右侧有主要细节视图。看起来ContentView像这样:

struct ContentView: View {
    @EnvironmentObject var store: Store
    @SceneStorage("selection") private var selectedGardenID: Garden.ID?
    @AppStorage("defaultGarden") private var defaultGardenID: Garden.ID?

    var body: some View {
        NavigationView {
            Sidebar(selection: selection)
            GardenDetail(garden: selectedGarden)
        }
    }

    private var selection: Binding<Garden.ID?> {
        Binding(get: { selectedGardenID ?? defaultGardenID }, set: { selectedGardenID = $0 })
    }

    private var selectedGarden: Binding<Garden> {
        $store[selection.wrappedValue]
    }
}
Run Code Online (Sandbox Code Playgroud)

和侧边栏是这样的:

struct Sidebar: View {
    @EnvironmentObject var store: Store
    @SceneStorage("expansionState") var expansionState = ExpansionState()
    @Binding var selection: Garden.ID?

    var body: some View {
        List(selection: $selection) {
            DisclosureGroup(isExpanded: $expansionState[store.currentYear]) {
                ForEach(store.gardens(in: store.currentYear)) { garden in
                    SidebarLabel(garden: garden)
                        .badge(garden.numberOfPlantsNeedingWater)
                }
            } label: {
                Label("Current", systemImage: "chart.bar.doc.horizontal")
            }

            Section("History") {
                GardenHistoryOutline(range: store.previousYears, expansionState: $expansionState)
            }
        }
        .frame(minWidth: 250)
    }
}
Run Code Online (Sandbox Code Playgroud)

现在SidebarLabel视图没有任何对选择绑定的引用;然而,当您单击其中一个标签时,选择会更新,并且绑定会传播到ContentView,因此GardenDetai视图会使用新的选择进行更新。

我的问题是:由于没有对选择绑定的引用,单击侧边栏项目之一如何导致选择绑定更新?ForEach和它有什么关系吗?

ForEach如果是这样,我如何添加不参与此类活动的“临时”项目?如果不是,那是怎么回事?

Chr*_*isR 5

ListForEach在这里一起工作。列表中看到里面有一个ForEach over Garden,ForEach自动提供了一个.tag(garden.id)

纯粹的List形式看起来像这样,并且可能更清楚地显示发生的情况。我包含了一个显式的内容.tag(),但这将自动生成。

struct Sidebar2: View {
    @EnvironmentObject var store: Store
    @Binding var selection: Garden.ID?

    var body: some View {
        List(store.gardens(in: store.currentYear), selection: $selection) { garden in
                    SidebarLabel(garden: garden)
                        .tag(garden.id) // << not needed, will be done implicitly
                        .badge(garden.numberOfPlantsNeedingWater)
        }
        .frame(minWidth: 250)
    }
}
Run Code Online (Sandbox Code Playgroud)

由于这些部分,List被用作 的包装器ForEach。但原理还是一样的。双手ForEach自动生成idList并设置selection

如果您想将自定义元素添加到侧边栏,您可以给它们一个明确的.tag...您只需找出在哪里解码这些自定义标签以及在详细视图中相应显示的内容。